This commit is contained in:
Amirreza
2026-07-05 22:04:36 +03:30
parent 0bcea0354c
commit aae7a68cda
283 changed files with 0 additions and 60050 deletions

View File

@@ -1,14 +0,0 @@
{
"env": {
"commonjs": true,
"es2021": true,
"node": true
},
"extends": "standard",
"parserOptions": {
"ecmaVersion": "latest"
},
"rules": {
"indent": ["error", 2]
}
}

View File

@@ -1,4 +0,0 @@
node_modules/
dist/
.env
.vscode/

View File

@@ -1,2 +0,0 @@
const startMongoDB = require('./mongo')
startMongoDB()

View File

@@ -1,24 +0,0 @@
// const mongoose = require('mongoose')
// const { MONGO_DBNAME, MONGO_HOST, MONGO_PORT } = process.env
// mongoose.connection.on('error', (error) => {
// console.log('mongodb connection failed! ', error.message)
// })
// const startMongoDB = () => {
// mongoose.connect(`mongodb://${MONGO_HOST}:${MONGO_PORT}/${MONGO_DBNAME}`, {
// })
// }
// module.exports = startMongoDB
const mongoose = require('mongoose')
mongoose.connection.on('error', (error) => {
console.log('mongodb connection failed! ', error.message)
})
mongoose.connection.on('open', function () {
console.log('Database Connected! ')
})
const startMongoDB = () => {
mongoose.connect(process.env.DATABASE_URL, {
authSource: 'admin'
})
}
module.exports = startMongoDB

View File

@@ -1,74 +0,0 @@
const AcademyCategoryModel = require('../../../models/AcademyCategoryModel');
// GET all categories
exports.getAll = async (req, res) => {
try {
const categories = await AcademyCategoryModel.find({}).sort({ createdAt: -1 });
res.status(200).json({ success: true, categories });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// CREATE category
exports.create = async (req, res) => {
try {
const { title, status } = req.body;
if (!title) {
return res.status(400).json({ success: false, message: "title is required" });
}
const newCategory = new AcademyCategoryModel({
title,
status: status ?? true,
});
await newCategory.save();
res.status(201).json({ success: true, category: newCategory });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// UPDATE category
exports.update = async (req, res) => {
try {
const { id } = req.params;
const { title, status } = req.body;
const updated = await AcademyCategoryModel.findByIdAndUpdate(
id,
{ title, status },
{ new: true, runValidators: true }
);
if (!updated) {
return res.status(404).json({ success: false, message: "category not found" });
}
res.status(200).json({ success: true, category: updated });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// DELETE
exports.remove = async (req, res) => {
try {
const { id } = req.params;
const deleted = await AcademyCategoryModel.findByIdAndDelete(id);
if (!deleted) {
return res.status(404).json({ success: false, message: "category not found" });
}
res.status(200).json({
success: true,
message: 'category deleted successfully'
});
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};

View File

@@ -1,72 +0,0 @@
const moment = require('moment-jalaali')
const MessageModel = require('../models/MessageModel')
const UserModel = require('../models/UserModel')
function formatTimeOnly(date) {
return moment(date).locale('fa').format('HH:mm:ss')
}
async function enrichMessage(message) {
const doc = message._doc ? { ...message._doc } : { ...message }
let replyTo = null
if (doc.replyToId) {
const parent = await MessageModel.findById(doc.replyToId).lean()
if (parent) {
const parentUser =
String(parent.senderId) === String(doc.senderId)
? await UserModel.findById(doc.senderId).select('first_name last_name user_name').lean()
: await UserModel.findById(parent.senderId).select('first_name last_name user_name').lean()
replyTo = {
_id: parent._id,
content: (parent.content || 'پیام').slice(0, 200),
senderName: parentUser
? `${parentUser.first_name || ''} ${parentUser.last_name || ''}`.trim() || parentUser.user_name
: 'کاربر'
}
}
}
return {
...doc,
createdAt: formatTimeOnly(doc.createdAt),
replyTo,
forwardedFrom: doc.forwardedFrom || undefined
}
}
function emitChatEvents(io, message, senderId, receiverId) {
const payload = message
const roomA = `chat:${senderId}:${receiverId}`
const roomB = `chat:${receiverId}:${senderId}`
io.to(roomA).to(roomB).emit('newMessage', payload)
io.to(`user:${senderId}`).to(`user:${receiverId}`).emit('chatListUpdate', {
senderId,
receiverId,
message: payload
})
}
function parseForwardedContent(content) {
if (!content) return { body: '', forwardedFrom: null }
try {
const line = content.split('\n')[0]
const j = JSON.parse(line)
if (j.forwardedFrom) {
return {
forwardedFrom: j.forwardedFrom,
body: content.replace(/^\{.*\}\n?/, '').trim()
}
}
} catch (_) {
/* not json */
}
return { body: content, forwardedFrom: null }
}
module.exports = {
formatTimeOnly,
enrichMessage,
emitChatEvents,
parseForwardedContent
}

View File

@@ -1,357 +0,0 @@
const express = require('express');
const app = express();
const path = require('path');
const http = require('http').createServer(app);
const cors = require('cors');
const moment = require('moment-jalaali');
const MessageModel = require('./models/MessageModel');
const fs = require('fs-extra');
const cron = require('node-cron');
const UserModel = require('./models/UserModel');
const blockCheck = require('./middlewares/blockCheck');
const rateLimit = require('express-rate-limit');
const { default: axios } = require('axios');
const allowedOrigins = [
'http://localhost:3000',
'http://localhost:3001', // اضافه شد — مهم برای توسعه فرانت
'http://localhost:3002',
'http://localhost:3003',
'http://localhost',
'https://modstagram.com',
'https://www.modstagram.com',
'https://api.modstagram.com',
'https://panel.modstagram.com',
'https://www.panel.modstagram.com',
'https://panel.modstagram.ir',
'https://www.panel.modstagram.ir',
'ionic://localhost',
'capacitor://localhost',
// IPهای محلی (برای تست در شبکه داخلی)
/^http:\/\/192\.168\.\d{1,3}\.\d{1,3}:\d+$/,
/^http:\/\/10\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/,
];
app.use(cors({
origin: (origin, callback) => {
// در محیط توسعه، همه originها مجاز باشن
if (process.env.NODE_ENV !== 'production') {
return callback(null, true);
}
// اگر origin وجود نداشته باشه (مثل درخواست از موبایل یا Postman) اجازه بده
if (!origin) {
return callback(null, true);
}
// چک لیست یا RegExp
const isAllowed = allowedOrigins.some(item => {
if (typeof item === 'string') return item === origin;
if (item instanceof RegExp) return item.test(origin);
return false;
});
if (isAllowed) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'x-no-toast'],
credentials: true,
optionsSuccessStatus: 200 // برای مرورگرهای قدیمی
}));
// Socket.IO CORS
const io = require('socket.io')(http, {
cors: {
origin: (origin, callback) => {
if (process.env.NODE_ENV !== 'production') {
return callback(null, true);
}
if (!origin) {
return callback(null, true);
}
const isAllowed = allowedOrigins.some(item => {
if (typeof item === 'string') return item === origin;
if (item instanceof RegExp) return item.test(origin);
return false;
});
if (isAllowed) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'],
credentials: true
}
});
// افزایش limit برای Base64 (1000mb کافیه)
app.use(express.json({ limit: '1000mb' }));
app.use(express.urlencoded({ limit: '1000mb', extended: true }));
app.use('/storage', express.static(path.join(__dirname, 'storage')));
app.use('/storage/profiles', express.static(path.join(__dirname, '../storage/profiles')));
app.use('/storage/carts', express.static(path.join(__dirname, '../storage/carts')));
app.use('/storage/posts', express.static(path.join(__dirname, '../storage/posts')));
app.use('/storage/messages', express.static(path.join(__dirname, '../storage/messages')));
app.use('/storage/tickets', express.static(path.join(__dirname, '../storage/tickets')));
app.use('/storage/advertising', express.static(path.join(__dirname, '../storage/advertising')));
app.use('/storage/services', express.static(path.join(__dirname, '../storage/services')));
require('./boot');
// غیرفعال کردن پارس‌کننده‌های بدنه برای روت‌های آپلود فایل
app.use((req, res, next) => {
if (req.path === '/api/v1/posts/create' && req.method === 'POST') {
console.log('DEBUG: Skipping body parsers for /api/v1/posts/create');
return next();
}
express.json()(req, res, next);
});
app.use((req, res, next) => {
if (req.path === '/api/v1/posts/create' && req.method === 'POST') {
return next();
}
express.urlencoded({ extended: true })(req, res, next);
});
require('./middlewares')(app);
const limiter = rateLimit({
windowMs: 3 * 60 * 1000,
max: 3000,
standardHeaders: true,
legacyHeaders: false,
message: 'تعداد درخواست‌های شما بیشتر از حد مجاز است، لطفاً بعداً دوباره تلاش کنید.',
handler: (req, res) => {
const retryAfter = Math.ceil((req.rateLimit.resetTime - Date.now()) / 1000);
res.set('Retry-After', retryAfter);
res.status(429).json({
error: `شما بیش از حد مجاز درخواست ارسال کرده‌اید. لطفاً بعد از ${retryAfter} ثانیه دوباره تلاش کنید.`
});
}
});
app.use(limiter);
app.set('trust proxy', 'loopback');
require('./routes')(app);
const { enrichMessage, emitChatEvents, parseForwardedContent } = require('./helpers/chatHelpers')
app.get('/api/v1/chat', async (req, res) => {
try {
const { senderId, receiverId, limit, page } = req.query
const pageNumber = parseInt(page) || 1
const limitPerPage = parseInt(limit) || 40
const filter = {
$or: [
{ senderId, receiverId },
{ senderId: receiverId, receiverId: senderId }
]
}
const totalMessagesCount = await MessageModel.countDocuments(filter)
const totalPages = Math.ceil(totalMessagesCount / limitPerPage) || 1
const skip = (pageNumber - 1) * limitPerPage
const messages = await MessageModel.find(filter)
.sort({ createdAt: -1 })
.skip(skip)
.limit(limitPerPage)
await MessageModel.updateMany(
{ _id: { $in: messages.map((m) => m._id) }, receiverId: senderId },
{ $set: { readStatus: 1 } }
)
const enriched = await Promise.all(messages.map((m) => enrichMessage(m)))
res.json({ messages: enriched.reverse(), totalPages })
} catch (error) {
console.error('Error fetching messages:', error)
res.status(500).json({ error: 'Server error' })
}
})
app.post('/api/v1/chat', [blockCheck], async (req, res) => {
try {
const { senderId, receiverId, content, replyToId, forwardedFrom: fwdBody } = req.body
const { body, forwardedFrom: fwdParsed } = parseForwardedContent(content)
const payload = {
senderId,
receiverId,
content: body || content,
replyToId: replyToId || undefined,
forwardedFrom: fwdBody || fwdParsed || undefined
}
const newMessage = new MessageModel(payload)
await newMessage.save()
const formatted = await enrichMessage(newMessage)
res.status(201).json({ data: formatted })
emitChatEvents(io, formatted, senderId, receiverId)
} catch (error) {
console.error('Error sending message:', error)
res.status(500).json({ error: 'Server error' })
}
})
app.get('/api/v1/chat/read', async (req, res) => {
try {
const { senderId, receiverId } = req.query // شناسه فرستنده و گیرنده از درخواست دریافت شود
// پیدا کردن تمام پیام‌هایی که کاربر دوم مشاهده کرده است و کاربر اول آنها را ارسال کرده است
const messages = await MessageModel.find(
{ senderId: receiverId, receiverId: senderId } // برای مواقعی که شناسه‌ها معکوس باشند
)
// به روزرسانی وضعیت خوانده شده یا نشده بودن پیام‌ها به "خوانده شده"
await MessageModel.updateMany({ _id: { $in: messages.map(message => message._id) } }, { $set: { readStatus: 1 } })
res.status(200).json({ message: 'Message read status updated successfully' })
} catch (error) {
console.error('Error updating message read status:', error)
res.status(500).json({ error: 'Server error' })
}
})
app.post("/api/v1/notification/send-sms", async (req, res) => {
const { receiverId, message } = req.body;
const user = await UserModel.findById(receiverId);
if (!user || !user.mobile) return res.status(404).json({ error: "User not found" });
console.log("3");
const mobile = user.mobile
const user_name = user.user_name
const data = JSON.stringify({
mobile,
templateId: '876533',
parameters: [
{ name: 'USER', value: user_name }
]
})
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) {
console.log(response);
})
.catch(function (error) {
console.log(error)
})
res.status(200).json({ success: true });
});
app.post('/api/v1/chat/file', [blockCheck], async (req, res) => {
try {
const { senderId, receiverId, content, replyToId, fileType } = req.body
const { file } = req.files
let fileUrl = null
if (file) {
const uploadDir = path.join(__dirname, '../storage/messages')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
const uniqueFileName = `${Date.now()}-${file.name}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(file.path, filePath)
fileUrl = `/messages/${uniqueFileName}`
}
const newMessage = new MessageModel({
senderId,
receiverId,
content: content || '',
file: fileUrl,
fileType: fileType || undefined,
replyToId: replyToId || undefined
})
await newMessage.save()
const formatted = await enrichMessage(newMessage)
res.status(201).json({ data: formatted })
emitChatEvents(io, formatted, senderId, receiverId)
} catch (error) {
console.error('Error sending message:', error)
res.status(500).json({ error: 'Server error' })
}
})
// Errors
require('./middlewares/exception')(app)
require('./middlewares/404')(app)
io.on('connection', (socket) => {
socket.on('joinUser', ({ userId }) => {
if (userId) socket.join(`user:${userId}`)
})
socket.on('joinChat', ({ userId, receiverId }) => {
if (userId && receiverId) {
socket.join(`chat:${userId}:${receiverId}`)
socket.join(`chat:${receiverId}:${userId}`)
}
})
socket.on('typing', ({ senderId, receiverId }) => {
io.to(`chat:${receiverId}:${senderId}`).emit('userTyping', { userId: senderId })
})
socket.on('stopTyping', ({ senderId, receiverId }) => {
io.to(`chat:${receiverId}:${senderId}`).emit('userStoppedTyping', { userId: senderId })
})
socket.on('messageSeen', async ({ messageId, senderId, receiverId }) => {
try {
if (messageId) {
await MessageModel.findByIdAndUpdate(messageId, { readStatus: 1 })
io.to(`chat:${receiverId}:${senderId}`).emit('messageStatusUpdate', {
messageId,
status: 'seen'
})
}
} catch (e) {
console.error('messageSeen error', e)
}
})
})
// Free Project
require('./services/AdvertisingCron')
require('./services/ProjectCron')
cron.schedule('0 0 1 * *', async () => {
try {
await UserModel.updateMany({}, {
daily_free_request: 1,
last_free_request_date: new Date(),
monthly_free_offer: 1,
last_free_offer_date: new Date()
})
console.log('Monthly free requests reset for all users.')
} catch (error) {
console.error('Error resetting daily free requests:', error)
}
}, {
scheduled: true,
timezone: 'Asia/Tehran'
})
module.exports = (port) => {
http.listen(port, () => {
console.log(`HTTP server is running on port ${port}`)
})
}

View File

@@ -1,190 +0,0 @@
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const timestamp = require('mongoose-timestamp');
const AcademyCategorySchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'عنوان دسته‌بندی الزامی است'],
trim: true,
minLength: [2, 'عنوان باید حداقل ۲ کاراکتر باشد'],
maxLength: [100, 'عنوان باید حداکثر ۱۰۰ کاراکتر باشد'],
unique: true,
index: true,
},
slug: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true,
index: true,
},
description: {
type: String,
required: false,
trim: true,
maxLength: 500,
default: null,
},
icon: {
type: String,
required: false,
default: null,
},
color: {
type: String,
required: false,
default: '#3B82F6',
match: /^#[0-9A-F]{6}$/i,
},
parent: {
type: mongoose.Schema.Types.ObjectId,
ref: 'AcademyCategory',
required: false,
default: null,
index: true,
},
level: {
type: Number,
required: true,
default: 0,
min: 0,
max: 3,
},
order: {
type: Number,
required: true,
default: 0,
index: true,
},
image: {
type: String,
required: false,
default: null,
},
course_count: {
type: Number,
required: true,
default: 0,
min: 0,
},
status: {
type: String,
enum: ['active', 'inactive', 'deleted'],
required: true,
default: 'active',
index: true,
},
is_featured: {
type: Boolean,
required: true,
default: false,
index: true,
},
seo_title: {
type: String,
required: false,
trim: true,
maxLength: 70,
},
seo_description: {
type: String,
required: false,
trim: true,
maxLength: 160,
},
created_by: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
updated_by: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: false,
},
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
// Virtual برای زیردسته‌ها
AcademyCategorySchema.virtual('children', {
ref: 'AcademyCategory',
localField: '_id',
foreignField: 'parent',
justOne: false,
options: { sort: { order: 1, title: 1 } }
});
// Virtual برای دوره‌های این دسته
AcademyCategorySchema.virtual('courses', {
ref: 'Course',
localField: '_id',
foreignField: 'category_id',
justOne: false,
match: { status: 'accept' }
});
// Middleware: قبل از save
AcademyCategorySchema.pre('save', async function(next) {
if (this.isModified('title') || this.isNew) {
// تولید slug
this.slug = this.title
.replace(/[^\u0600-\u06FF\uFB8A\u067E\u0686\u06AF\u200C\uFB8E\u0698a-zA-Z0-9\s]/g, '')
.trim()
.replace(/\s+/g, '-')
.toLowerCase();
// اگه parent داره، سطح رو محاسبه کن
if (this.parent) {
const parent = await this.constructor.findById(this.parent);
if (parent) {
this.level = parent.level + 1;
}
} else {
this.level = 0;
}
}
next();
});
// استاتیک متدها
AcademyCategorySchema.statics = {
// دریافت درخت دسته‌بندی‌ها
async getTree() {
const categories = await this.find({ status: 'active', parent: null })
.sort({ order: 1, title: 1 })
.populate({
path: 'children',
match: { status: 'active' },
options: { sort: { order: 1, title: 1 } },
populate: {
path: 'children',
match: { status: 'active' },
options: { sort: { order: 1, title: 1 } }
}
});
return categories;
},
// افزایش تعداد دوره‌ها
async incrementCourseCount(categoryId, increment = 1) {
return this.findByIdAndUpdate(categoryId, {
$inc: { course_count: increment }
});
},
// دریافت دسته‌بندی با اسلاگ
async getBySlug(slug) {
return this.findOne({ slug, status: 'active' })
.populate('children')
.populate('parent');
}
};
AcademyCategorySchema.plugin(timestamp);
AcademyCategorySchema.plugin(mongoosePaginate);
module.exports = mongoose.model('AcademyCategory', AcademyCategorySchema);

View File

@@ -1,71 +0,0 @@
const mongoose = require('mongoose');
const timestamp = require('mongoose-timestamp');
const mongoosePaginate = require('mongoose-paginate-v2');
const modelSchema = new mongoose.Schema({
course_images: {
type: [String], // آرایه‌ای از مسیرهای تصاویر
required: false,
default: [],
},
course_video: {
type: String, // مسیر ویدئو
required: false,
trim: true,
default: null,
},
type: {
type: String, // نوع پست: 'image' یا 'video'
required: true,
enum: ['image', 'video'],
},
files: [
{
path: { type: String, required: false },
type: { type: String, enum: ['image', 'video'], required: false },
}
],
caption: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 1000,
default: null,
},
status: {
type: String,
required: false,
trim: true,
enum: ['pending', 'accept', 'reject'],
default: 'accept',
},
user_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
likes: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
}],
comments: {
type: Object,
default: null,
},
courseId: {
type: String,
},
is_free: {
type: Boolean,
},
file_name: {
type: String,
},
});
modelSchema.plugin(timestamp);
modelSchema.plugin(mongoosePaginate);
const PostModel = mongoose.model('AcademyContent', modelSchema);
module.exports = PostModel;

View File

@@ -1,25 +0,0 @@
const mongoose = require("mongoose");
const timestamp = require("mongoose-timestamp");
const mongoosePaginate = require("mongoose-paginate-v2");
const modelSchema = new mongoose.Schema({
user_id: {
type: String,
required: true,
trim: true,
default: 0,
},
course_id: {
type: String,
required: true,
trim: true,
default: 0,
},
});
modelSchema.plugin(timestamp);
modelSchema.plugin(mongoosePaginate);
const PostModel = mongoose.model("Likes", modelSchema);
module.exports = PostModel;

View File

@@ -1,55 +0,0 @@
const mongoose = require("mongoose");
const timestamp = require("mongoose-timestamp");
const mongoosePaginate = require("mongoose-paginate-v2");
const modelSchema = new mongoose.Schema({
academy_image: {
type: String,
required: false,
trim: true,
default: null,
},
academy_name: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null,
},
userId: {
type: String,
required: true,
},
rate: {
type: Number,
trim: true,
default: 0
},
number_of_rate: {
type: Number,
trim: true,
default: 0
},
sheba: {
type: String,
required: false,
default: null
},
bio: {
type: String,
required: false,
default: null
},
tag: {
type: Object,
required: false,
default: null
},
});
modelSchema.plugin(timestamp);
modelSchema.plugin(mongoosePaginate);
const PostModel = mongoose.model("Academy", modelSchema);
module.exports = PostModel;

View File

@@ -1,24 +0,0 @@
const mongoose = require("mongoose");
const timestamp = require("mongoose-timestamp");
const mongoosePaginate = require("mongoose-paginate-v2");
const modelSchema = new mongoose.Schema({
price: { type: Number, required: true }, // قیمت نهایی شامل مالیات
discountAmount: { type: Number, default: 0 }, // مبلغ تخفیف
taxAmount: { type: Number, default: 0 }, // مبلغ مالیات
taxRate: { type: Number, default: 9 }, // درصد مالیات
course_name: { type: String, required: true },
course_id: { type: mongoose.Schema.Types.ObjectId, ref: "Course", required: true },
academy_id: { type: mongoose.Schema.Types.ObjectId, ref: "Academy", required: true },
user_id: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
payment_authority: { type: String },
payment_ref_id: { type: String },
status: { type: String, enum: ["pending", "success", "failed", "settled"], default: "success" },
});
modelSchema.plugin(timestamp);
modelSchema.plugin(mongoosePaginate);
const PostModel = mongoose.model("AcademyPayment", modelSchema);
module.exports = PostModel;

View File

@@ -1,80 +0,0 @@
const mongoose = require('mongoose')
const mongoosePaginate = require('mongoose-paginate-v2')
const timestamp = require('mongoose-timestamp')
const adminSchema = new mongoose.Schema({
mobile: {
type: String,
required: false,
trim: true,
unique: true
},
otp: {
type: String,
required: false,
trim: true,
minLength: 6,
maxLength: 6
},
user_name: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
password: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
first_name: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
last_name: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
user_type: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
profile_image: {
type: String,
required: false,
trim: true
},
gender: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
last_online: {
type: Date, // ذخیره تاریخ و زمان آخرین بازدید
default: Date.now // تنظیم تاریخ به تاریخ فعلی
}
})
adminSchema.plugin(timestamp)
adminSchema.plugin(mongoosePaginate)
const AdminModel = mongoose.model('Admin', adminSchema)
module.exports = AdminModel

View File

@@ -1,23 +0,0 @@
const mongoose = require('mongoose')
const mongoosePaginate = require('mongoose-paginate-v2')
const timestamp = require('mongoose-timestamp')
const advertisingCategorySchema = new mongoose.Schema({
title: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
status: {
type: Boolean,
required: false,
default: true
}
})
advertisingCategorySchema.plugin(timestamp)
advertisingCategorySchema.plugin(mongoosePaginate)
const AdvertisingCategoryModel = mongoose.model('AdvertisingCategory', advertisingCategorySchema)
module.exports = AdvertisingCategoryModel

View File

@@ -1,32 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const mongoosePaginate = require('mongoose-paginate-v2')
const advertisingCommentSchema = new mongoose.Schema({
advertisingId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Advertising',
required: true
},
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
text: {
type: String,
required: true,
trim: true
},
status: {
type: String,
enum: ['pending', 'accepted', 'rejected'],
default: 'pending'
}
})
advertisingCommentSchema.plugin(timestamp)
advertisingCommentSchema.plugin(mongoosePaginate)
const AdvertisingComment = mongoose.model('AdvertisingComment', advertisingCommentSchema)
module.exports = AdvertisingComment

View File

@@ -1,27 +0,0 @@
const mongoose = require('mongoose')
const mongoosePaginate = require('mongoose-paginate-v2')
const timestamp = require('mongoose-timestamp')
const advertisingFeaturesSchema = new mongoose.Schema({
title: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
status: {
type: Boolean,
required: false,
default: true
},
type: {
type: String,
default: 'boolean'
}
})
advertisingFeaturesSchema.plugin(timestamp)
advertisingFeaturesSchema.plugin(mongoosePaginate)
const AdvertisingFeaturesModel = mongoose.model('AdvertisingFeatures', advertisingFeaturesSchema)
module.exports = AdvertisingFeaturesModel

View File

@@ -1,20 +0,0 @@
const mongoose = require('mongoose')
const advertisingLikeSchema = new mongoose.Schema({
advertisingId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Advertising',
required: true
},
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
}
})
// تعریف مدل AdvertisingLikeModel
const AdvertisingLikeModel = mongoose.model('AdvertisingLike', advertisingLikeSchema)
module.exports = AdvertisingLikeModel

View File

@@ -1,230 +0,0 @@
const mongoose = require('mongoose')
const mongoosePaginate = require('mongoose-paginate-v2')
const timestamp = require('mongoose-timestamp')
const advertisingSchema = new mongoose.Schema({
title: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
category: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
description: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 1024,
default: null
},
province: {
type: Object,
default: null
},
city: {
type: Object,
default: null
},
neighbourhood: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
address: {
type: String,
trim: true,
minLength: 1,
maxLength: 512,
default: null
},
lat: {
type: String,
trim: true,
minLength: 1,
maxLength: 256,
default: null
},
lng: {
type: String,
trim: true,
minLength: 1,
maxLength: 256,
default: null
},
images: [{
type: String,
required: false,
trim: true,
default: null
}],
services: [{
title: {
type: String,
required: true,
trim: true
},
originalPrice: {
type: String,
required: true
},
discountPrice: {
type: String,
required: false
},
discountPercentage: {
type: Number,
required: false
},
status: {
type: String,
default: "UnderReview",
required: false
},
image: {
type: String,
required: false,
trim: true
}
}],
features: [{
title: {
type: String,
required: false,
trim: true
},
value: {
type: Boolean,
required: true
}
}],
contactInfo: {
phone: {
type: String,
required: false,
trim: true,
default: null
},
mobile: {
type: String,
required: false,
trim: true,
default: null
},
telegramLink: {
type: String,
required: false,
trim: true,
default: null
},
whatsappNumber: {
type: String,
required: false,
trim: true,
default: null
},
instagramLink: {
type: String,
required: false,
trim: true,
default: null
},
saveInfoForNextAds: {
type: Boolean,
required: true,
default: false
}
},
type: {
type: String,
enum: ['free', 'normal', 'special', 'highlight'],
default: 'normal'
},
status: {
type: String,
enum: ['pre_payment', 'paid', 'accepted', 'rejected', 'expired'],
default: 'pre_payment'
},
payment_status: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
creator_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
reject_reason: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 1024,
default: null
},
mostDiscountPercentage: {
type: Number,
required: false
},
showDiscount: {
type: Boolean,
required: false,
default: false
},
likesCount: {
type: Number,
default: 0
},
commentsCount: {
type: Number,
default: 0
},
ratings: [{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
rating: {
type: Number,
required: true,
min: 1,
max: 5
}
}],
averageRating: {
type: Number,
default: 0
},
ratingsCount: {
type: Number,
default: 0
},
viewCount: {
type: Number,
default: 0
},
acceptedAt: {
type: Date,
default: null
}
})
advertisingSchema.plugin(timestamp)
advertisingSchema.plugin(mongoosePaginate)
const AdvertisingModel = mongoose.model('Advertising', advertisingSchema)
module.exports = AdvertisingModel

View File

@@ -1,113 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const mongoosePaginate = require('mongoose-paginate-v2')
const advertisingProfileSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
profile_image: {
type: String,
required: false,
trim: true,
default: null
},
image_count: {
type: Number,
default: 0
},
like_count: {
type: Number,
default: 0
},
comment_count: {
type: Number,
default: 0
},
vitrine_name: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
category: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
about_us: {
type: String,
required: false,
minLength: 1,
maxLength: 1024,
default: null
},
province: {
type: Object,
default: null
},
city: {
type: Object,
default: null
},
lat: {
type: String,
trim: true,
minLength: 1,
maxLength: 512,
default: null
},
lng: {
type: String,
trim: true,
minLength: 1,
maxLength: 512,
default: null
},
address: {
type: String,
required: false,
trim: true,
default: null
},
neighbourhood: {
type: String,
required: false,
trim: true,
default: null
},
adTotalRatings: {
type: Number,
default: 0
},
adRatingsCount: {
type: Number,
default: 0
},
adAverageRating: {
type: Number,
default: 0
},
contactInfo: {
phone: String,
mobile: String,
telegramLink: String,
whatsappNumber: String,
instagramLink: String,
saveInfoForNextAds: { type: Boolean, default: false }
}
})
advertisingProfileSchema.plugin(timestamp)
advertisingProfileSchema.plugin(mongoosePaginate)
const AdvertisingProfileModel = mongoose.model('AdvertisingProfile', advertisingProfileSchema)
module.exports = AdvertisingProfileModel

View File

@@ -1,26 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const advertisingRatingSchema = new mongoose.Schema({
advertisingId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Advertising',
required: true
},
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
rating: {
type: Number,
required: true,
min: 1,
max: 5
}
})
advertisingRatingSchema.plugin(timestamp)
const AdvertisingRatingModel = mongoose.model('AdvertisingRating', advertisingRatingSchema)
module.exports = AdvertisingRatingModel

View File

@@ -1,25 +0,0 @@
const mongoose = require('mongoose')
const advertisingTypeSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
minLength: 1,
maxLength: 255
},
price: {
type: Number,
required: true,
trim: true,
minLength: 1,
maxLength: 255
},
status: {
type: String,
default: 'active'
}
})
const AdvertisingTypeModel = mongoose.model('AdvertisingType', advertisingTypeSchema)
module.exports = AdvertisingTypeModel

View File

@@ -1,54 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const mongoosePaginate = require('mongoose-paginate-v2')
const commentSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
project: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Project',
required: false
},
offer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Offer',
required: false
},
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
rating: {
type: Number,
required: true
},
comment: {
type: String,
trim: true
},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post',
required: false
},
comment_for: {
type: String,
enum: ['user', 'project', 'post'],
required: true
},
status: {
type: String,
enum: ['pending', 'accepted', 'rejected'],
default: 'pending'
}
})
commentSchema.plugin(timestamp)
commentSchema.plugin(mongoosePaginate)
const CommentModel = mongoose.model('Comment', commentSchema)
module.exports = CommentModel

View File

@@ -1,40 +0,0 @@
const mongoose = require("mongoose");
const timestamp = require("mongoose-timestamp");
const mongoosePaginate = require("mongoose-paginate-v2");
const modelSchema = new mongoose.Schema({
user_id: {
type: String,
required: true,
trim: true,
},
course_id: {
type: String,
required: true,
trim: true,
},
comment: {
type: String,
required: true,
trim: true,
default: 0,
},
rate: {
type: String,
required: true,
trim: true,
default: 0,
},
status: {
type: String,
enum: ['pending', 'accepted', 'rejected'],
default: 'accepted'
}
});
modelSchema.plugin(timestamp);
modelSchema.plugin(mongoosePaginate);
const PostModel = mongoose.model("CourseComment", modelSchema);
module.exports = PostModel;

View File

@@ -1,79 +0,0 @@
const mongoose = require("mongoose");
const timestamp = require("mongoose-timestamp");
const mongoosePaginate = require("mongoose-paginate-v2");
const modelSchema = new mongoose.Schema({
price: {
type: String,
required: true,
},
cuorse_name: {
type: String,
required: true,
},
category:{
type: String
},
offer: {
type: String
},
academyId:{
type: String
},
caption: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 1000,
default: null,
},
status: {
type: String,
required: false,
trim: true,
enum: ['pending', 'accept', 'reject'],
default: 'accept',
},
user_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
likes: {
type: Number,
required: false,
trim: true,
default: 0,
},
comments: {
type: Object,
default: null,
},
type: {
type: String,
required: false,
trim: true,
enum: ['normal', 'pro', 'legend'],
default: "normal",
},
course_time: {
type: String
},
number_of_course_content: {
type: String
},
teacher_number: {
type: String
},
course_image: {
type: String
},
teacher_name: {
type: String
},
});
modelSchema.plugin(timestamp);
modelSchema.plugin(mongoosePaginate);
const PostModel = mongoose.model("Course", modelSchema);
module.exports = PostModel;

View File

@@ -1,22 +0,0 @@
const mongoose = require("mongoose");
const timestamp = require("mongoose-timestamp");
const mongoosePaginate = require("mongoose-paginate-v2");
const modelSchema = new mongoose.Schema({
price: {
type: String,
required: true,
},
cuorse_type: {
type: String,
required: false,
trim: true,
enum: ['normal', 'pro', 'legend'],
},
});
modelSchema.plugin(timestamp);
modelSchema.plugin(mongoosePaginate);
const PostModel = mongoose.model("CoursePayment", modelSchema);
module.exports = PostModel;

View File

@@ -1,47 +0,0 @@
// const mongoose = require('mongoose')
// const timestamp = require('mongoose-timestamp')
// const subExpertiseSchema = new mongoose.Schema({
// id: {
// type: mongoose.Schema.Types.ObjectId,
// ref: 'ExpertiseModel'
// },
// name: {
// type: String
// }
// })
// const expertiseSchema = new mongoose.Schema({
// expertise: {
// type: String,
// unique: true
// },
// sub_expertise: [subExpertiseSchema]
// })
// expertiseSchema.plugin(timestamp)
// const ExpertiseModel = mongoose.model('Expertise', expertiseSchema)
// module.exports = ExpertiseModel
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const subExpertiseSchema = new mongoose.Schema({
name: {
type: String
}
})
const expertiseSchema = new mongoose.Schema({
expertise: {
type: String,
unique: true
},
sub_expertise: [subExpertiseSchema]
})
expertiseSchema.plugin(timestamp)
const ExpertiseModel = mongoose.model('Expertise', expertiseSchema)
module.exports = ExpertiseModel

View File

@@ -1,15 +0,0 @@
const mongoose = require("mongoose");
const timestamp = require("mongoose-timestamp");
const mongoosePaginate = require("mongoose-paginate-v2");
const modelSchema = new mongoose.Schema({
user_id: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
course_id: { type: mongoose.Schema.Types.ObjectId, ref: "Course", required: true },
price_paid: { type: Number, required: true }, // قیمت پرداخت شده
purchased_at: { type: Date, default: Date.now },
});
modelSchema.plugin(timestamp);
modelSchema.plugin(mongoosePaginate);
const PostModel = mongoose.model("IsPaymentCourse", modelSchema);
module.exports = PostModel;

View File

@@ -1,25 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const likeSchema = new mongoose.Schema({
postId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post',
required: true
},
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
likes: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}]
})
// تعریف مدل LikeModel
likeSchema.plugin(timestamp)
const LikeModel = mongoose.model('Like', likeSchema)
module.exports = LikeModel

View File

@@ -1,51 +0,0 @@
const mongoose = require('mongoose')
const { Schema } = mongoose
const messageSchema = new Schema({
senderId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
receiverId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
content: {
type: String,
required: false,
trim: true
},
file: {
type: String,
required: false
},
fileType: {
type: String,
enum: ['image', 'video', 'file', 'voice', 'location'],
required: false
},
replyToId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Message',
required: false
},
forwardedFrom: {
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
userName: String,
displayName: String
},
createdAt: {
type: Date,
default: Date.now
},
readStatus: {
type: Number,
default: 0
}
})
const MessageModel = mongoose.model('Message', messageSchema)
module.exports = MessageModel

View File

@@ -1,37 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const mongoosePaginate = require('mongoose-paginate-v2')
const notificationSchema = new mongoose.Schema({
user_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User', // مرجع به مدل کاربر
required: true
},
project_post_id: {
type: mongoose.Schema.Types.ObjectId,
required: false
},
type: {
type: String,
required: true
},
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
read: {
type: Boolean,
default: false // وضعیت خوانده شده یا نشده
}
})
notificationSchema.plugin(timestamp)
notificationSchema.plugin(mongoosePaginate)
const NotificationModel = mongoose.model('Notification', notificationSchema)
module.exports = NotificationModel

View File

@@ -1,32 +0,0 @@
const mongoose = require('mongoose')
const mongoosePaginate = require('mongoose-paginate-v2')
const timestamp = require('mongoose-timestamp')
const offerSchema = new mongoose.Schema({
sender: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
receiver: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
status: {
type: String,
enum: ['pending', 'accepted', 'rejected'], // وضعیت درخواست
default: 'pending'
},
transaction_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Payment',
default: null
}
})
offerSchema.plugin(timestamp)
offerSchema.plugin(mongoosePaginate)
const OfferModel = mongoose.model('Offer', offerSchema)
module.exports = OfferModel

View File

@@ -1,21 +0,0 @@
const mongoose = require('mongoose')
const offerTypeModelSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
minLength: 1,
maxLength: 255
},
price: {
type: Number,
required: true,
trim: true,
minLength: 1,
maxLength: 255
}
})
const OfferTypeModel = mongoose.model('OfferTypeModel', offerTypeModelSchema)
module.exports = OfferTypeModel

View File

@@ -1,46 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const mongoosePaginate = require('mongoose-paginate-v2')
const paymentSchema = new mongoose.Schema({
amount: {
type: Number,
required: true
},
status: {
type: String,
enum: ['pending', 'successful', 'failed'],
default: 'pending'
},
authority: {
type: String,
required: false
},
user_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
project_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Project'
},
advertising_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Advertising'
},
type: {
type: String,
required: true
},
installment_step: {
type: String,
required: false,
default: null
}
})
paymentSchema.plugin(timestamp)
paymentSchema.plugin(mongoosePaginate)
const PaymentModel = mongoose.model('Payment', paymentSchema)
module.exports = PaymentModel

View File

@@ -1,60 +0,0 @@
const mongoose = require('mongoose');
const timestamp = require('mongoose-timestamp');
const mongoosePaginate = require('mongoose-paginate-v2');
const modelSchema = new mongoose.Schema({
post_images: {
type: [String], // آرایه‌ای از مسیرهای تصاویر
required: false,
default: [],
},
post_video: {
type: String, // مسیر ویدئو
required: false,
trim: true,
default: null,
},
type: {
type: String, // نوع پست: 'image' یا 'video'
required: true,
enum: ['image', 'video'],
},
files: [
{
path: { type: String, required: true },
type: { type: String, enum: ['image', 'video'], required: true },
}
],
caption: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 1000,
default: null,
},
status: {
type: String,
required: false,
trim: true,
enum: ['pending', 'accept', 'reject'],
default: 'pending',
},
user_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
likes: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
}],
comments: {
type: Object,
default: null,
},
});
modelSchema.plugin(timestamp);
modelSchema.plugin(mongoosePaginate);
const PostModel = mongoose.model('Post', modelSchema);
module.exports = PostModel;

View File

@@ -1,186 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const mongoosePaginate = require('mongoose-paginate-v2')
const projectSchema = new mongoose.Schema({
title: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
expertise: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
sub_expertise: {
type: [String], // تغییر نوع به آرایه از استرینگ
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
gender: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
age: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
conversation_projects: {
type: Boolean,
required: false,
default: null
},
province: {
type: Object,
default: null
},
city: {
type: Object,
default: null
},
offer_time: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
offer_price: {
type: Number,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
description: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 1024,
default: null
},
project_type: {
type: String,
enum: ['free', 'normal', 'force', 'highlight'],
default: 'normal'
},
payment_status: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
// فیلد برای وضعیت پروژه
status: {
type: String,
enum: ['pre_payment', 'paid', 'accepted', 'rejected', 'done', 'cancled', 'ongoing'],
default: 'pre_payment'
},
public_status: {
type: String,
enum: ['public', 'private'],
default: 'public'
},
created_for_user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
default: null
},
creator_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
requested_users: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
selected_user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
default: null
},
final_price: {
type: Number,
required: false,
default: null
},
final_time: {
type: Number,
required: false,
default: null
},
installments: [{
installment_number: {
type: Number,
required: true
},
amount: {
type: Number,
required: true
},
due_date: {
type: Date,
required: true
}
}],
reject_reason: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 1024,
default: null
},
ratings: [{
creator_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
rating: {
type: Number,
required: true
},
comment: {
type: String,
trim: true
}
}],
acceptedAt: {
type: Date,
default: null
},
isExpired: {
type: Boolean,
default: false
}
})
projectSchema.plugin(timestamp)
// اضافه کردن پیجینیشن به مدل
projectSchema.plugin(mongoosePaginate)
const ProjectModel = mongoose.model('Project', projectSchema)
module.exports = ProjectModel

View File

@@ -1,33 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const requestSchema = new mongoose.Schema({
project: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Project',
required: true
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
time: {
type: Number,
required: true
},
price: {
type: Number,
required: true
},
status: {
type: String,
enum: ['pending', 'accepted', 'rejected'],
default: 'pending'
}
})
requestSchema.plugin(timestamp)
const RequestModel = mongoose.model('Request', requestSchema)
module.exports = RequestModel

View File

@@ -1,19 +0,0 @@
// models/Setting.js
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const settingSchema = new mongoose.Schema({
key: {
type: String,
required: true,
unique: true
},
value: {
type: String,
required: true
}
})
settingSchema.plugin(timestamp)
const SettingModel = mongoose.model('Setting', settingSchema)
module.exports = SettingModel

View File

@@ -1,19 +0,0 @@
const mongoose = require('mongoose')
const provincesSchema = new mongoose.Schema({
id: Number,
name: String,
slug: String
})
const citiesSchema = new mongoose.Schema({
id: Number,
name: String,
slug: String,
province_id: Number
})
const ProvinceModel = mongoose.model('Province', provincesSchema)
const CityModel = mongoose.model('City', citiesSchema)
module.exports = { ProvinceModel, CityModel }

View File

@@ -1,15 +0,0 @@
const mongoose = require("mongoose");
const timestamp = require("mongoose-timestamp");
const mongoosePaginate = require("mongoose-paginate-v2");
const modelSchema = new mongoose.Schema({
type: { type: String, enum: ["course", "product", "service"], required: true },
tax: { type: Number, required: true, default: 9 }, // درصد مالیات
is_active: { type: Boolean, default: true },
});
modelSchema.plugin(timestamp);
modelSchema.plugin(mongoosePaginate);
const PostModel = mongoose.model("Tax", modelSchema);
module.exports = PostModel;

View File

@@ -1,20 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const mongoosePaginate = require('mongoose-paginate-v2')
const ticketMessageSchema = new mongoose.Schema({
text: { type: String, required: false },
file: {
type: String, // مسیر فایل در ذخیره شود
required: false // فایل اختیاری است
},
senderType: { type: String, enum: ['User', 'Admin'], required: true },
senderId: { type: mongoose.Schema.Types.ObjectId, refPath: 'senderType', required: true },
ticket: { type: mongoose.Schema.Types.ObjectId, ref: 'Ticket', required: true }
})
ticketMessageSchema.plugin(timestamp)
// اضافه کردن پیجینیشن به مدل
ticketMessageSchema.plugin(mongoosePaginate)
const TicketMessageModel = mongoose.model('TicketMessage', ticketMessageSchema)
module.exports = TicketMessageModel

View File

@@ -1,24 +0,0 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
const mongoosePaginate = require('mongoose-paginate-v2')
const ticketSchema = new mongoose.Schema({
title: { type: String, required: true },
status: { type: String, enum: ['Pending', 'Answered', 'Customer Response', 'Closed'], default: 'Pending' },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
new_message: {
type: Boolean,
default: false // وضعیت خوانده شده یا نشده
}
})
ticketSchema.pre('save', function (next) {
this.updatedAt = Date.now()
next()
})
ticketSchema.plugin(timestamp)
// اضافه کردن پیجینیشن به مدل
ticketSchema.plugin(mongoosePaginate)
const TicketModel = mongoose.model('Ticket', ticketSchema)
module.exports = TicketModel

View File

@@ -1,21 +0,0 @@
const mongoose = require('mongoose')
const typeAndPriceSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
minLength: 1,
maxLength: 255
},
price: {
type: Number,
required: true,
trim: true,
minLength: 1,
maxLength: 255
}
})
const TypeAndPriceModel = mongoose.model('TypeAndPrice', typeAndPriceSchema)
module.exports = TypeAndPriceModel

View File

@@ -1,381 +0,0 @@
const mongoose = require('mongoose')
const mongoosePaginate = require('mongoose-paginate-v2')
const timestamp = require('mongoose-timestamp')
const userSchema = new mongoose.Schema({
mobile: {
type: String,
required: false,
trim: true,
unique: true
},
otp: {
type: String,
required: false,
trim: true,
minLength: 6,
maxLength: 6,
default: null
},
user_name: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
password: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
first_name: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
last_name: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
user_type: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
is_verified: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: 'none'
},
profile_image: {
type: String,
required: false,
trim: true,
default: null
},
national_card_image: {
type: String,
required: false,
trim: true,
default: null
},
expertise: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
sub_expertise: {
type: [String], // تغییر نوع به آرایه از استرینگ
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
gender: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
height: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
weight: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
size: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
eye_color: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
hair_color: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
cooperation_type: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
cooperation_abroad: {
type: Boolean,
required: false,
default: null
},
conversation_projects: {
type: Boolean,
required: false,
default: null
},
bio: {
type: String,
required: false,
minLength: 1,
maxLength: 1024,
default: null
},
national_code: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
birthday: {
type: Date,
required: false,
default: null
},
is_Register: {
type: String,
default: false
},
shaba: {
type: String,
required: false,
trim: true,
minLength: 1,
maxLength: 255,
default: null
},
address: {
type: String,
trim: true,
minLength: 1,
maxLength: 512,
default: null
},
province: {
type: Object,
default: null
},
city: {
type: Object,
default: null
},
lat: {
type: String,
trim: true,
minLength: 1,
maxLength: 512,
default: null
},
lng: {
type: String,
trim: true,
minLength: 1,
maxLength: 512,
default: null
},
show_location: {
type: Boolean,
required: false,
default: null
},
rate: {
type: String,
trim: true,
maxLength: 512,
default: null
},
referral_code: {
type: String,
trim: true,
minLength: 1,
maxLength: 512,
default: null
},
shop_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Shop',
default: null
},
last_online: {
type: Date, // ذخیره تاریخ و زمان آخرین بازدید
default: Date.now // تنظیم تاریخ به تاریخ فعلی
},
business_license: {
type: String,
required: false,
trim: true,
default: null
},
user_level: {
type: String,
trim: true,
minLength: 1,
maxLength: 512,
default: null
},
user_score: {
type: String,
trim: true,
minLength: 1,
maxLength: 512,
default: null
},
last_post: {
type: Object,
default: null
},
// user_ratings: [{
// project_id: {
// type: mongoose.Schema.Types.ObjectId,
// ref: 'Project',
// required: true
// },
// creator_id: {
// type: mongoose.Schema.Types.ObjectId,
// ref: 'User',
// required: true
// },
// rating: {
// type: Number,
// required: true
// },
// comment: {
// type: String,
// trim: true
// }
// }],
daily_free_request: {
type: Number,
default: 1
},
last_free_request_date: {
type: Date,
default: null
},
blocked_by: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
blocked_users: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
block_status: {
type: Boolean,
required: false,
default: null
},
advertising_profiles: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'AdvertisingProfile'
}],
services: [{
title: {
type: String,
required: true,
trim: true
},
originalPrice: {
type: String,
required: true
},
discountPrice: {
type: String,
required: false
},
discountPercentage: {
type: Number,
required: false
},
status: {
type: String,
default: "UnderReview",
required: false
},
image: {
type: String,
required: false,
trim: true
}
}],
monthly_free_offer: {
type: Number,
default: 1
},
last_free_offer_date: {
type: Date,
default: null
}
// contactInfo: {
// phone: String,
// mobile: String,
// telegramLink: String,
// whatsappNumber: String,
// instagramLink: String,
// saveInfoForNextAds: { type: Boolean, default: false }
// },
// adTotalRatings: {
// type: Number,
// default: 0
// },
// adRatingsCount: {
// type: Number,
// default: 0
// },
// adAverageRating: {
// type: Number,
// default: 0
// }
})
userSchema.plugin(timestamp)
userSchema.plugin(mongoosePaginate)
const UserModel = mongoose.model('User', userSchema)
module.exports = UserModel

View File

@@ -1,12 +0,0 @@
// models/VersionModel.js
const mongoose = require('mongoose')
const versionSchema = new mongoose.Schema({
version: { type: String, required: true },
mandatory: { type: Boolean, required: true },
releaseNotes: { type: String },
updateUrls: { type: [String], required: true } // لینک‌های آپدیت
})
const VersionModel = mongoose.model('Version', versionSchema)
module.exports = VersionModel

View File

@@ -1,14 +0,0 @@
const mongoose = require('mongoose')
const mongoosePaginate = require('mongoose-paginate-v2')
const timestamp = require('mongoose-timestamp')
const license = new mongoose.Schema({
userId : String,
Confirmation : { type : Boolean, default : false},
licenseImg : { type : String },
})
license.plugin(timestamp)
license.plugin(mongoosePaginate)
const LicenseMoldel = mongoose.model('license', license)
module.exports = LicenseMoldel

View File

@@ -1,16 +0,0 @@
const { CityModel } = require('../../models/StateCity')
const getCities = async (req, res, next) => {
try {
const { id } = req.params
const cities = await CityModel.find({ province_id: id })
res.status(200).json({
cities
})
} catch (err) {
res.status(500).send('Internal Server Error')
}
}
module.exports = {
getCities
}

View File

@@ -1,13 +0,0 @@
/* eslint-disable camelcase */
const ExpertiseModel = require('../../../models/ExpertiseModel')
const getExpertise = async (req, res, next) => {
try {
const expertises = await ExpertiseModel.find().select('expertise sub_expertise')
res.json({ expertises })
} catch (error) {
next(error)
}
}
module.exports = {
getExpertise
}

View File

@@ -1,65 +0,0 @@
const jwt = require('jsonwebtoken')
const PaymentModel = require('../../../models/PaymentModel')
const ProjectModel = require('../../../models/ProjectModel')
const moment = require('moment-jalaali')
const getFinancial = async (req, res) => {
try {
// Extract user ID from JWT token
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 options = {
page: req.query.page || 1, // صفحه پیش‌فرض ۱
limit: req.query.limit || 10, // حداکثر تعداد موارد برای هر صفحه
sort: { createdAt: -1 }
}
// Find all payments associated with the user
const financial = await PaymentModel.paginate(
{ user_id: userId }, // فیلتر
options // گزینه‌های پیجینیشن
)
// Prepare the data to be sent back
const userPayments = await Promise.all(financial.docs.map(async (payment) => {
// Find the project associated with the payment
const project = await ProjectModel.findById(payment.project_id)
if (!project) {
return null
}
// استفاده از کتابخانه moment-jalaali برای تبدیل تاریخ و زمان به شمسی
const shamsiDate = moment(payment.createdAt).format('jYYYY-jMM-jDD')
const shamsiTime = moment(payment.createdAt).format('HH:mm')
// Extract the required information
const userPayment = {
_id: payment._id,
project_title: project.title,
project_id: project._id,
payment_date: shamsiDate, // Format date as YYYY-MM-DD
payment_time: shamsiTime,
iban: project.creator_id ? project.creator_id.iban : null, // Check if creator_id exists before accessing iban
amount: payment.amount,
payment_status: payment.status
}
return userPayment
}))
// Filter out null values
const validUserPayments = userPayments.filter(payment => payment !== null)
// Send the payments back to the client
res.status(200).json({
financial: financial,
totalPages: financial.totalPages, // ارسال تعداد کل صفحات
totalItems: financial.totalDocs // ارسال تعداد کل آیتم‌ها
})
} catch (error) {
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
module.exports = { getFinancial }

View File

@@ -1,77 +0,0 @@
/* eslint-disable camelcase */
const bcrypt = require('bcryptjs')
const { check, validationResult } = require('express-validator')
const jwt = require('jsonwebtoken')
const UserModel = require('../../../models/UserModel')
const changePasswordValidationRules = () => {
return [
check('new_password')
.notEmpty().withMessage('رمز عبور نمی‌تواند خالی باشد')
.isLength({ min: 8, max: 25 }).withMessage('رمز عبور نباید کوتاه تر از 8 کاراکتر باشد')
.matches(/^(?=.*[a-zA-Zآ-ی])(?=.*\d).{8,}$/u).withMessage('رمز عبور باید شامل حروف و اعداد باشد')
]
}
const changePasswordUser = async (req, res, next) => {
try {
const { new_password } = req.body
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 errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
// یافتن کاربر با شماره موبایل
const user = await UserModel.findById(userId)
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
// هش کردن پسورد جدید و ذخیره در دیتابیس
const hashedPassword = await bcrypt.hash(new_password, 10)
user.password = hashedPassword
await user.save()
return res.json({
message: 'کلمه عبور با موفقیت ذخیره شد'
})
} catch (error) {
next(error)
}
}
const editChangePasswordUser = async (req, res, next) => {
try {
const { mobile, new_password } = req.body
const user = await UserModel.findOne({ mobile })
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
// هش کردن پسورد جدید و ذخیره در دیتابیس
const hashedPassword = await bcrypt.hash(new_password, 10)
user.password = hashedPassword
await user.save()
return res.json({
message: 'کلمه عبور با موفقیت تغییر یافت'
})
} catch (error) {
next(error)
}
}
module.exports = {
changePasswordUser, changePasswordValidationRules, editChangePasswordUser
}

View File

@@ -1,87 +0,0 @@
const UserModel = require('../../../models/UserModel')
const { check, validationResult } = require('express-validator')
const { default: axios } = require('axios')
const loginValidationRules = () => {
return [
check('mobile')
.notEmpty().withMessage('شماره موبایل نمی‌تواند خالی باشد')
.isLength({ min: 11, max: 11 }).withMessage('شماره موبایل باید دقیقاً 11 رقم باشد')
.matches(/^09[0-9]{9}$/).withMessage('فرمت شماره موبایل صحیح نیست')
]
}
const loginUser = async (req, res, next) => {
try {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const { mobile } = req.body
if (!mobile) {
return res.status(422).json({
error: true,
message: 'شماره موبایل نمی‌تواند خالی باشد'
})
}
// const existingUser = await UserModel.findOne({ mobile })
// if (!existingUser) {
// return res.status(422).json({ error: true, message: 'کاربری با این شماره موبایل یافت نشد' })
// }
function generateOTP () {
return Math.floor(100000 + Math.random() * 900000)
}
const otp = generateOTP()
// eslint-disable-next-line no-unused-vars
const user = await UserModel.findOneAndUpdate(
{ mobile },
{ $set: { mobile, otp } },
{ upsert: true, new: true, lean: true }
)
// زمانبندی تنظیم مقدار otp به null پس از 10 دقیقه
setTimeout(() => {
UserModel.findOneAndUpdate({ mobile }, { $set: { otp: null } }, { new: true })
.then(() => {})
.catch(error => console.error('Error setting OTP to null:', error))
}, 5 * 60 * 1000)
const data = JSON.stringify({
mobile,
templateId: '930719',
parameters: [
{ name: 'CODE', value: otp.toString() }
]
})
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)
})
res.status(200).json({
success: true,
message: 'کد تایید ارسال شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
loginValidationRules,
loginUser
}

View File

@@ -1,92 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const bcrypt = require('bcryptjs')
const TokenService = require('../../../services/TokenService')
// const jwt = require('jsonwebtoken')
const loginWithUserName = async (req, res, next) => {
try {
// اعتبارسنجی ورودی ها
const { user_name, password } = req.body
if (!user_name || !password) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// یافتن کاربر با نام کاربری
const userLow = user_name.toLowerCase()
const user = await UserModel.findOne({ user_name: userLow })
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این نام کاربری یافت نشد'
})
}
if (!user.password) {
return res.status(422).json({
error: true,
message: 'رمز عبور برای این کاربر تنظیم نشده است'
})
}
const isPasswordValid = await bcrypt.compare(password, user.password)
if (!isPasswordValid) {
return res.status(422).json({
error: true,
message: 'نام کاربری یا رمز عبور اشتباه است'
})
}
// اگر هم نام کاربری و هم رمز عبور درست بود، ارسال پیام موفقیت آمیز
const token = TokenService.sign({ id: user._id })
// if (user.user_name === null) {
// return res.json({
// message: 'ورود با موفقیت',
// page: 'username'
// })
// }
// if (!user.first_name && !user.last_name) {
// return res.json({
// message: 'ورود با موفقیت',
// page: 'name'
// })
// }
// if (!user.user_type) {
// return res.json({
// message: 'کد تایید صحیح بود',
// page: 'usertype'
// })
// }
let step = ''
if (user.user_name === null) { step = 'user_name' } else if
(!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 || !user.last_name) {
return res.json({
message: 'کد تایید صحیح بود',
token,
step,
user_type: null,
page: 'auth-page',
id: user._id
})
}
return res.json({
message: 'کد تایید صحیح بود',
token,
page: 'home',
step,
user_type: user.user_type,
id: user._id
})
} catch (error) {
next(error)
}
}
module.exports = {
loginWithUserName
}

View File

@@ -1,89 +0,0 @@
const UserModel = require('../../../models/UserModel')
const TokenService = require('../../../services/TokenService')
// const jwt = require('jsonwebtoken')
const verifyUser = async (req, res, next) => {
try {
const { mobile, otp } = req.body
if (!mobile || !otp) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const user = await UserModel.findOne({ mobile })
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
const token = TokenService.sign({ id: user._id })
if (user.otp === otp) {
// if (user.user_name === null) {
// return res.json({
// message: 'کد تایید صحیح بود',
// page: 'username'
// })
// }
// if (!user.first_name && !user.last_name) {
// return res.json({
// message: 'کد تایید صحیح بود',
// page: 'name'
// })
// }
// if (!user.user_type) {
// return res.json({
// message: 'کد تایید صحیح بود',
// page: 'usertype'
// })
// }
// اگر همه فیلدها پر بودند
// return res.json({
// message: 'کد تایید صحیح بود',
// token,
// user_type: user.user_type,
// page: 'home',
// id: user._id
// })
let step = ''
if (user.user_name === null) { step = 'user_name' } else if
(!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 || !user.last_name) {
return res.json({
message: 'کد تایید صحیح بود',
token,
step,
user_type: null,
page: 'auth-page',
id: user._id
})
}
return res.json({
message: 'کد تایید صحیح بود',
token,
user_type: user.user_type,
step,
page: 'home',
id: user._id
})
}
return res.status(422).json({
error: true,
message: 'کد تایید اشتباه است'
})
} catch (error) {
next(error)
}
}
module.exports = {
verifyUser
}

View File

@@ -1,127 +0,0 @@
const MessageModel = require('../../../models/MessageModel')
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const moment = require('moment-jalaali')
const getMessages = 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 { search } = req.query
// پیدا کردن تمام پیام‌هایی که کاربر مورد نظر آنها فرستنده یا گیرنده بوده است
const messageFilter = {
$or: [
{ senderId: userId },
{ receiverId: userId }
]
}
// پیدا کردن تمام پیام‌ها با فیلتر و سورت براساس جدیدترین پیام
const messages = await MessageModel.find(messageFilter).sort({ createdAt: -1 })
// استخراج شناسه‌های تمام کاربران مرتبط با این پیام‌ها (بدون تکرار)
const userIdToLastMessageDate = {}
messages.forEach(message => {
if (!userIdToLastMessageDate[message.senderId]) {
userIdToLastMessageDate[message.senderId] = message.createdAt
}
if (!userIdToLastMessageDate[message.receiverId]) {
userIdToLastMessageDate[message.receiverId] = message.createdAt
}
})
// تبدیل مجموعه شناسه‌های کاربر به آرایه
const userIdsArray = Object.keys(userIdToLastMessageDate)
// ساخت فیلتر جستجو برای کاربران مرتبط
const userFilter = { _id: { $in: userIdsArray } }
if (search) {
userFilter.$and = [
{ _id: { $in: userIdsArray } }, // فقط کاربران مرتبط
{
$or: [
{ first_name: { $regex: search, $options: 'i' } },
{ last_name: { $regex: search, $options: 'i' } },
{ user_name: { $regex: search, $options: 'i' } }
]
}
]
}
// پیدا کردن اطلاعات کاربران مرتبط بر اساس فیلتر جستجو و شمارش تعداد پیام‌های خوانده نشده برای هر کاربر
const usersData = await UserModel.find(userFilter)
const usersWithUnreadCount = await Promise.all(usersData.map(async (user) => {
const unreadMessagesCount = await MessageModel.countDocuments({ receiverId: userId, senderId: user._id, readStatus: 0 })
let lastOnlineStatus
const now = moment()
const lastOnline = moment(user.last_online)
const diffMinutes = now.diff(lastOnline, 'minutes')
if (diffMinutes < 1) {
lastOnlineStatus = 'آنلاین'
} else if (diffMinutes < 60) {
lastOnlineStatus = `${diffMinutes} دقیقه پیش`
} else if (diffMinutes < 1440) {
lastOnlineStatus = `${Math.floor(diffMinutes / 60)} ساعت پیش`
} else {
lastOnlineStatus = `${Math.floor(diffMinutes / 1440)} روز پیش`
}
// بررسی آیا کاربر فعلی در لیست بلاک‌شده‌های این کاربر است یا خیر
const blockedByCurrentUser = user.blocked_by.includes(userId)
const blockedYou = user.blocked_users.includes(userId)
return {
first_name: user.first_name,
last_name: user.last_name,
user_name: user.user_name,
is_verified: user.is_verified,
profile_image: user.profile_image,
last_online: lastOnlineStatus,
_id: user._id,
unread_messages_count: unreadMessagesCount,
is_blocked: blockedByCurrentUser,
blocked_you: blockedYou,
last_message_date: userIdToLastMessageDate[user._id]
}
}))
// حذف کاربر فعلی از آرایه و سورت کردن کاربران براساس آخرین پیام
const filteredUsersData = usersWithUnreadCount
.filter(user => user._id.toString() !== userId)
.sort((a, b) => new Date(b.last_message_date) - new Date(a.last_message_date))
res.status(200).json({
filteredUsersData,
totalMessages: messages.length
})
} catch (error) {
next(error)
}
}
const getUnreadMessages = 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 unreadMessagesCount = await MessageModel.countDocuments({ receiverId: userId, readStatus: 0 })
res.status(200).json({ unread_messages_count: unreadMessagesCount })
} catch (error) {
next(error)
}
}
module.exports = {
getMessages, getUnreadMessages
}

View File

@@ -1,68 +0,0 @@
const jwt = require('jsonwebtoken')
const NotificationModel = require('../../../models/NotificationModel')
const jMoment = require('moment-jalaali')
const getNotifications = 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 { page = 1, limit = 10, search } = req.query // افزودن پارامترهای صفحه‌بندی به درخواست
const filter = { user_id: userId }
if (search) {
filter.$or = [
{ title: { $regex: search, $options: 'i' } },
{ description: { $regex: search, $options: 'i' } }
]
}
// استفاده از مدل پیجینیت شده برای دریافت نتایج صفحه‌بندی شده
const options = {
page: parseInt(page), // تبدیل صفحه به عدد صحیح
limit: parseInt(limit), // تبدیل محدودیت به عدد صحیح
sort: { createdAt: -1 } // بر اساس زمان ایجاد (createdAt) مرتب کنید (نزولی)
}
// دریافت اعلان‌های مربوط به کاربر با استفاده از userId
let notifications = await NotificationModel.paginate(filter, options)
const totalPages = notifications.totalPages
const totalItems = notifications.totalDocs
notifications = notifications?.docs.map(notification => {
const jDate = jMoment(notification.createdAt).format('jYYYY-jMM-jDD HH:mm')
return {
...notification._doc,
createdAt: jDate
}
})
// تغییر وضعیت "read" اعلان‌ها به "true"
await NotificationModel.updateMany({ user_id: userId }, { read: true })
res.json({
notifications,
totalPages, // ارسال تعداد کل صفحات
totalItems // ارسال تعداد کل آیتم‌ها
})
} catch (error) {
next(error)
}
}
const unreadNotifications = 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 newNotificationsCount = await NotificationModel.countDocuments({ user_id: userId, read: false }).exec()
res.json({ newNotificationsCount })
} catch (error) {
next(error)
}
}
module.exports = {
getNotifications, unreadNotifications
}

View File

@@ -1,439 +0,0 @@
/* eslint-disable camelcase */
const jwt = require('jsonwebtoken')
const jMoment = require('moment-jalaali')
const OfferModel = require('../../../models/OfferModel')
const UserModel = require('../../../models/UserModel')
const OfferTypeModel = require('../../../models/OfferTypeModel')
const PaymentModel = require('../../../models/PaymentModel')
const NotificationModel = require('../../../models/NotificationModel')
const { default: axios } = require('axios')
const CommentModel = require('../../../models/CommentModel')
const getOfferPriceFromDatabase = async (itemName) => {
const item = await OfferTypeModel.findOne({ name: itemName })
if (!item) {
throw new Error('Item not found in database')
}
return item.price
}
const getOfferTypes = 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 offerTypes = await OfferTypeModel.find({}, 'name price')
// بررسی برای مواردی مانند پیدا نشدن انواع پروژه
if (!offerTypes) {
return res.status(404).json({ message: 'انواع پروژه یافت نشد.' })
}
// اگر کاربر درخواست ماهانه نداشته باشد، نوع پروژه ماهانه را حذف کنید
if (user.monthly_free_offer <= 0) {
offerTypes = offerTypes.filter(projectType => projectType.name !== 'free')
}
// ارسال انواع پروژه به کاربر
res.status(200).json({ offerTypes })
} catch (error) {
// در صورت بروز خطا، ارسال پیام خطا به کاربر
console.error('Error in getOfferTypes:', error)
res.status(500).json({ message: 'خطا در دریافت انواع پروژه.' })
}
}
const getUserOffers = 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 { page = 1, limit = 10, status_filter, id } = req.query // افزودن پارامترهای صفحه‌بندی به درخواست
let filter = {}
let filterbyid = {receiver: id}
let filterbyidsender = {sender: id}
if (status_filter === 'درخواست') {
filter = { sender: userId }
} else {
filter = { receiver: userId }
}
// تنظیم گزینه‌های صفحه‌بندی
const options = {
page: parseInt(page), // تبدیل صفحه به عدد صحیح
limit: parseInt(limit), // تبدیل محدودیت به عدد صحیح
sort: { createdAt: -1 }, // بر اساس زمان ایجاد (createdAt) مرتب کنید (نزولی)
populate: [
{
path: 'sender',
select: 'profile_image user_level first_name last_name user_name is_verified user_score rate'
},
{
path: 'receiver',
select: 'profile_image user_level first_name last_name user_name is_verified user_score rate'
}
]
}
// دریافت آفرهای صفحه‌بندی شده
let offer = await OfferModel.paginate(filter, options)
let offerbyid = await OfferModel.paginate(filterbyid, options)
let offerbyidsender = await OfferModel.paginate(filterbyidsender, options)
const totalPages = offer.totalPages
const totalItems = offer.totalDocs
// فرمت کردن تاریخ و ارسال پاسخ
offer = offer?.docs.map(offer => {
const jDate = jMoment(offer.createdAt).format('jYYYY-jMM-jDD HH:mm')
return {
...offer._doc,
createdAt: jDate
}
})
res.json({
offerbyidsender,
offerbyid,
offer,
totalPages, // ارسال تعداد کل صفحات
totalItems // ارسال تعداد کل آیتم‌ها
})
} catch (error) {
next(error)
}
}
const updateOfferStatus = 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 // ID کاربری که درخواست را به‌روزرسانی می‌کند
const reciverUser = await UserModel.findById(userId)
const { offerId, action } = req.body // آیدی آفر و اکشن (accept یا reject)
// بررسی اعتبار ورودی‌ها
if (!offerId || !['accept', 'reject'].includes(action)) {
return res.status(400).json({ error: 'Invalid input' })
}
// یافتن آفر موردنظر
const offer = await OfferModel.findById(offerId)
const senderUser = await UserModel.findById(offer?.sender)
if (!offer) {
return res.status(404).json({ error: 'Offer not found' })
}
// بررسی اینکه آیا کاربر گیرنده این آفر است یا خیر
if (offer.receiver.toString() !== userId) {
return res.status(403).json({ error: 'You are not authorized to update this offer' })
}
// بررسی اینکه آیا آفر قبلاً به‌روزرسانی شده یا نه
if (offer.status !== 'pending') {
return res.status(400).json({ error: 'Offer has already been updated', message: 'وضعیت این درخواست قبلا تغییر کرده است.' })
}
// به‌روزرسانی وضعیت آفر بر اساس اکشن
offer.status = action === 'accept' ? 'accepted' : 'rejected'
await offer.save()
if (action === 'accept') {
const data = JSON.stringify({
mobile: senderUser?.mobile,
templateId: '633448',
parameters: [
{ name: 'USER', value: reciverUser?.first_name + ' ' + reciverUser?.last_name },
{ name: 'EMPLOYER', value: senderUser?.first_name + ' ' + senderUser?.last_name }
]
})
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)
})
} else {
const data = JSON.stringify({
mobile: senderUser?.mobile,
templateId: '682678',
parameters: [
{ name: 'USER', value: reciverUser?.first_name + ' ' + reciverUser?.last_name },
{ name: 'EMPLOYER', value: senderUser?.first_name + ' ' + senderUser?.last_name }
]
})
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)
})
}
res.status(200).json({
message: `Offer has been ${offer.status} successfully`,
offerId: offer._id,
status: offer.status
})
} catch (error) {
next(error)
}
}
// Controller function for handling successful advertising payment
const handleSuccessfulOfferPayment = async (req, res) => {
try {
const { reciverId, offerType } = req.body
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
// Check user's free offer eligibility
const user = await UserModel.findById(userId)
if (!user) return res.status(404).json({ error: 'User not found' })
// Check user's free offer eligibility
const reciverUser = await UserModel.findById(reciverId)
if (!user) return res.status(404).json({ error: 'User not found' })
let price
if (user.monthly_free_offer > 0 && offerType === 'free') {
// Use free offer and decrement counter
price = 0
user.monthly_free_offer -= 1
await user.save()
} else {
// Retrieve offer price based on type
price = await getOfferPriceFromDatabase(offerType)
// Create a new payment entry
const payment = new PaymentModel({
amount: Number(price),
status: 'successful',
authority: '', // Add authority if available
user_id: userId,
type: 'offer',
installment_step: null
})
await payment.save()
}
// Create a new payment entry
const payment = new PaymentModel({
amount: Number(price),
status: 'successful',
authority: '', // Add authority if available
user_id: userId,
type: 'offer',
installment_step: null
})
await payment.save()
// Create a new offer
const offer = new OfferModel({
sender: userId,
receiver: reciverId,
transaction_id: payment._id // Link the payment to the offer
})
await offer.save()
console.log(reciverUser?.first_name)
// Send notification to selected user
const notification = new NotificationModel({
user_id: reciverId,
project_post_id: offer?._id,
type: 'new_offer',
title: 'درخواست همکاری جدید',
description: `${reciverUser?.first_name} عزیز یک درخواست همکاری برای شما در مجموعه مدستاگرام ثبت شد`
})
await notification.save()
// Send Sms
const data = JSON.stringify({
mobile: reciverUser?.mobile,
templateId: '569006',
parameters: [
{ name: 'FIRSTNAME', value: reciverUser?.first_name },
{ name: 'LASTNAME', value: reciverUser?.last_name }
]
})
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)
})
// Send event based on payment status
// Emit a success event
if ('eventEmitter' in req.app) {
req.app.get('eventEmitter').emit('paymentSuccess', {
message: 'Payment and offer created successfully',
offerId: offer._id
})
} else {
console.error('Error: eventEmitter is not defined or not properly configured.')
}
res.status(200).json({
message: 'Payment and offer recorded successfully',
offerId: offer._id,
price
})
} catch (error) {
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
// Controller function for handling failed advertising payment
const handleFailedOfferPayment = async (req, res) => {
try {
const { offerType } = req.body
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 price = await getOfferPriceFromDatabase(offerType)
// Create a new payment entry
const payment = new PaymentModel({
amount: Number(price),
status: 'failed',
authority: '', // Add authority if available
user_id: userId,
type: 'offer',
installment_step: null
})
await payment.save()
// Emit a failure event
if ('eventEmitter' in req.app) {
req.app.get('eventEmitter').emit('paymentFailed', { message: 'Payment failed' })
} else {
console.error('Error: eventEmitter is not defined or not properly configured.')
}
res.status(200).json({ message: 'Payment failed', price })
} catch (error) {
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
const createOfferComment = 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 { offerId, comment, rate, user_id } = req.body
if (!offerId || !comment || !rate || !user_id) {
return res.status(400).send({ message: 'All fields are required' })
}
// بررسی اینکه آیا کاربر قبلاً برای این پیشنهاد نظر داده است
const existingComment = await CommentModel.findOne({
offer: offerId,
creator: userId
})
if (existingComment) {
return res.status(400).send({ message: 'شما قبلاً نظر خود را برای این درخواست ثبت کرده‌اید' })
}
const newComment = new CommentModel({
user: user_id,
offer: offerId,
creator: userId,
rating: rate,
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 = 'استاد'
}
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)
}
}
module.exports = {
getUserOffers,
getOfferTypes,
handleSuccessfulOfferPayment,
handleFailedOfferPayment,
updateOfferStatus,
createOfferComment
}

View File

@@ -1,935 +0,0 @@
/* eslint-disable eqeqeq */
const TypeAndPriceModel = require('../../../models/TypeAndPriceModel');
const ProjectModel = require('../../../models/ProjectModel');
const PaymentModel = require('../../../models/PaymentModel');
const jwt = require('jsonwebtoken');
const RequestModel = require('../../../models/RequestModel');
const NotificationModel = require('../../../models/NotificationModel');
const UserModel = require('../../../models/UserModel');
const AdvertisingTypeModel = require('../../../models/AdvertisingTypeModel');
const AdvertisingModel = require('../../../models/AdvertisingModel');
const OfferTypeModel = require('../../../models/OfferTypeModel');
const OfferModel = require('../../../models/OfferModel');
const { default: axios } = require('axios');
const apiKey = "c7c41e8a-918f-4741-bcd5-58f3bc51db73"; // اصلاح نام متغیر محیطی
// Function to get price based on item name from database
const getPriceFromDatabase = async (itemName) => {
const item = await TypeAndPriceModel.findOne({ name: itemName });
if (!item) {
throw new Error(`Item "${itemName}" not found in TypeAndPriceModel`);
}
return item.price;
};
const getAdvertisingPriceFromDatabase = async (itemName) => {
const item = await AdvertisingTypeModel.findOne({ name: itemName });
if (!item) {
throw new Error(`Item "${itemName}" not found in AdvertisingTypeModel`);
}
return item.price;
};
const getOfferPriceFromDatabase = async (offerType) => {
const item = await OfferTypeModel.findOne({ name: offerType });
if (!item) {
throw new Error(`Offer type "${offerType}" not found in OfferTypeModel`);
}
return item.price;
};
// Controller function for initiating payment
const initiatePaymentWeb = async (req, res) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
// eslint-disable-next-line no-unused-vars
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
const itemName = req.body.item_name // Get item name from request body
const price = await getPriceFromDatabase(itemName) // Get price from database
const projectId = req.body.projectId // Get project ID from request body
// Save project ID for later use in callback URL
req.session.projectId = projectId
const item = await TypeAndPriceModel.findOne({ name: itemName })
const user = await UserModel.findById(userId)
if (item.name === 'free' && item.price === 0) {
if (user.daily_free_request > 0) {
user.daily_free_request -= 1
await user.save()
const project = await ProjectModel.findById(projectId)
if (!project) {
res.status(404).send('Project not found')
return
}
project.payment_status = 'done' // Set project status to 'done'
project.status = 'paid' // Set project status to 'done'
await project.save()
res.status(200).json({ message: 'پروژه رایگان با موفقیت ایجاد شد', projectId, type: 'free' })
} else {
res.status(403).json({ message: 'شما قبلاً از درخواست رایگان امروز استفاده کرده‌اید.' })
}
} else {
const response = await axios.post('https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json', {
MerchantID: apiKey,
Amount: Number(price),
Description: `پرداخت برای سفارش شماره ${projectId}`,
CallbackURL: `${process.env.APP_URL}/projects/payment-web?projectId=${projectId}&itemName=${itemName}&userId=${userId}` // Assuming your backend URL for handling payment callback
})
const data = response.data
if (data.Status === 100) {
// Payment request was successful
const authority = data.Authority // The payment authority (شناسه پرداخت)
res.status(200).json({ authority })
} else {
// Payment request failed
console.error('Error occurred while initiating payment:', data)
res.status(500).json({ error: 'Error occurred while initiating payment' })
}
}
} catch (error) {
// Error occurred while getting price from database or sending the request
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
// Controller function for handling payment callback from ZarinPal
const handlePaymentCallbackWeb = async (req, res) => {
try {
// console.log(req.query)
// Extract payment status and authority from ZarinPal callback
const status = req.query.Status
const authority = req.query.Authority
const projectId = req.query.projectId // Get project ID from session
const itemName = req.query.itemName // Get project ID from session
const userId = req.query.userId // Get project ID from session
// Clear project ID from session
// Query ZarinPal API to verify payment status
const verificationUrl = 'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json'
const price = await getPriceFromDatabase(itemName) // Get price from database
const verificationResponse = await axios.post(verificationUrl, {
MerchantID: apiKey,
Authority: authority,
Amount: Number(price)
})
const verificationData = verificationResponse.data
if (verificationData.Status === 100) {
const payment = new PaymentModel({
amount: Number(price),
status: 'successful',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
project_id: projectId,
type: 'create',
installment_step: null
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
await payment.save()
// Payment verification is successful
// Now, update project status to 'done' or desired status
const project = await ProjectModel.findById(projectId)
if (!project) {
res.status(404).send('Project not found')
return
}
project.payment_status = 'done' // Set project status to 'done'
project.status = 'paid' // Set project status to 'done'
await project.save()
// Send event based on payment status
if ('eventEmitter' in req.app) {
if (status === 'OK') {
// Payment is successful
// Emit an event to be listened to in the frontend
// Example event name: 'paymentSuccess'
// Example data: { message: 'Payment successful' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Payment successful' })
} else {
// Payment is canceled or failed
// Emit an event to be listened to in the frontend
// Example event name: 'paymentFailed'
// Example data: { message: 'Payment canceled or failed' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentFailed', { message: 'Payment canceled or failed' })
}
} else {
console.error('Error: eventEmitter is not defined or not properly configured.')
}
// Redirect to the callback URL with the project ID
res.redirect(`https://modstagram.com/projects/payment/success?projectId=${projectId}&price=${price}&type=create`)
} else {
// Payment verification failed
const payment = new PaymentModel({
amount: Number(price),
status: 'failed',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
project_id: projectId,
type: 'create'
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
await payment.save()
res.redirect(`https://modstagram.com/projects/payment/failed?projectId=${projectId}&price=${price}&type=create`)
}
} catch (error) {
// Error occurred while verifying payment
console.error('Error occurred:', error)
res.status(500).send('Internal server error')
}
}
const paymentRequestAcceptWeb = async (req, res) => {
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 projectId = req.body.projectId
// Find the project
const project = await ProjectModel.findById(projectId)
if (!project) {
return res.status(404).json({ error: 'Project not found' })
}
if (project.status == 'ongoing') {
return res.status(400).json({ message: 'برای این پروژه از قبل کاربر انتخاب شده' })
}
// eslint-disable-next-line eqeqeq
if (userId != project.creator_id.toString()) {
return res.status(422).json({
error: true,
message: 'شما نمیتوانید این پروژه را ویرایش کنید'
})
}
// Get the final price from the request model
const requestId = req.body.requestId
const request = await RequestModel.findById(requestId)
if (!request) {
return res.status(404).json({ error: 'Request not found' })
}
request.status = 'accepted'
await request.save()
project.selected_user = request.user
project.status = 'ongoing'
project.final_price = request.price
project.final_time = request.time
await project.save()
// Send notification to selected user
const notification = new NotificationModel({
user_id: request.user,
project_post_id: projectId,
type: 'request_accepted',
title: 'انتخاب پیشنهاد',
description: `کارفرما پیشنهاد کاری شما برای پروژه ${project.title} را پذیرفت.`
})
await notification.save()
// Sms
const userReciver = await UserModel.findById(request.user)
const data = JSON.stringify({
mobile: userReciver?.mobile,
templateId: '302876',
parameters: [
{ name: 'USER', 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)
})
res.status(200).json({
message: 'درخواست با موفقیت تایید شد'
})
} catch (error) {
// Error occurred while getting price from database or sending the request
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
const initiateAdvertisingPaymentWeb = async (req, res) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
// eslint-disable-next-line no-unused-vars
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
const itemName = req.body.item_name // Get item name from request body
const price = await getAdvertisingPriceFromDatabase(itemName) // Get price from database
const advertisingId = req.body.advertisingId // Get project ID from request body
const showDiscount = req.body.showDiscount
// Save project ID for later use in callback URL
req.session.advertisingId = advertisingId
const item = await AdvertisingTypeModel.findOne({ name: itemName })
const user = await UserModel.findById(userId)
if (item.name === 'free' && item.price === 0) {
if (user.daily_free_request > 0) {
user.daily_free_request -= 1
await user.save()
const project = await AdvertisingModel.findById(advertisingId)
if (!project) {
res.status(404).send('Advertising not found')
return
}
project.payment_status = 'done' // Set project status to 'done'
project.status = 'paid' // Set project status to 'done'
await project.save()
res.status(200).json({ message: 'بیلبورد رایگان با موفقیت ایجاد شد', advertisingId, type: 'free' })
} else {
res.status(403).json({ message: 'شما قبلاً از بیلبورد رایگان این ماه استفاده کرده‌اید.' })
}
} else {
const response = await axios.post('https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json', {
MerchantID: process.env.ZARINPAL_MERCHANT_ID,
Amount: showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price),
Description: `پرداخت برای سفارش شماره ${advertisingId}`,
CallbackURL: `${process.env.APP_SITE_CallBack}/advertising/payment-web?advertisingId=${advertisingId}&itemName=${itemName}&userId=${userId}&showDiscount=${showDiscount}` // Assuming your backend URL for handling payment callback
})
const data = response.data
if (data.Status === 100) {
// Payment request was successful
const authority = data.Authority // The payment authority (شناسه پرداخت)
res.status(200).json({ authority })
} else {
// Payment request failed
console.error('Error occurred while initiating payment:', data)
res.status(500).json({ error: 'Error occurred while initiating payment' })
}
}
} catch (error) {
// Error occurred while getting price from database or sending the request
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
const handleAdvertisingPaymentCallbackWeb = async (req, res) => {
try {
// console.log(req.query)
// Extract payment status and authority from ZarinPal callback
const status = req.query.Status
const authority = req.query.Authority
const advertisingId = req.query.advertisingId // Get project ID from session
const itemName = req.query.itemName // Get project ID from session
const userId = req.query.userId // Get project ID from session
const showDiscount = req.query.showDiscount // Get project ID from session
// Clear project ID from session
// Query ZarinPal API to verify payment status
const verificationUrl = 'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json'
const price = await getAdvertisingPriceFromDatabase(itemName) // Get price from database
const verificationResponse = await axios.post(verificationUrl, {
MerchantID: apiKey,
Authority: authority,
Amount: showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price)
})
const verificationData = verificationResponse.data
if (verificationData.Status === 100) {
const payment = new PaymentModel({
amount: showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price),
status: 'successful',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
advertising_id: advertisingId,
type: 'advertising',
installment_step: null
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
await payment.save()
// Payment verification is successful
// Now, update project status to 'done' or desired status
const advertising = await AdvertisingModel.findById(advertisingId)
if (!advertising) {
res.status(404).send('Advertising not found')
return
}
advertising.payment_status = 'done' // Set advertising status to 'done'
advertising.status = 'paid' // Set advertising status to 'done'
await advertising.save()
// Send event based on payment status
if ('eventEmitter' in req.app) {
if (status === 'OK') {
// Payment is successful
// Emit an event to be listened to in the frontend
// Example event name: 'paymentSuccess'
// Example data: { message: 'Payment successful' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Payment successful' })
} else {
// Payment is canceled or failed
// Emit an event to be listened to in the frontend
// Example event name: 'paymentFailed'
// Example data: { message: 'Payment canceled or failed' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentFailed', { message: 'Payment canceled or failed' })
}
} else {
console.error('Error: eventEmitter is not defined or not properly configured.')
}
// Redirect to the callback URL with the advertising ID
res.redirect(`https://modstagram.com/billboards/payment/success?projectId=${advertisingId}&price=${showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price)}&type=advertising&showDiscount=${showDiscount}`)
} else {
// Payment verification failed
const payment = new PaymentModel({
amount: showDiscount ? Number(price + 20000) : Number(price),
status: 'failed',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
advertising_id: advertisingId,
type: 'advertising'
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
console.log('ss')
await payment.save()
res.redirect(`https://modstagram.com/billboards/payment/failed?projectId=${advertisingId}&price=${showDiscount === true || showDiscount === 'true' ? Number(price + 20000) : Number(price)}&type=advertising&showDiscount=${showDiscount}`)
}
} catch (error) {
// Error occurred while verifying payment
console.error('Error occurred:', error)
res.status(500).send('Internal server error')
}
}
const republishAdvertisingPaymentWeb = async (req, res) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
// eslint-disable-next-line no-unused-vars
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
const itemName = req.body.item_name // Get item name from request body
const price = await getAdvertisingPriceFromDatabase(itemName) // Get price from database
const advertisingId = req.body.advertisingId // Get project ID from request body
const showDiscount = req.body.showDiscount
// Save project ID for later use in callback URL
req.session.advertisingId = advertisingId
const item = await AdvertisingTypeModel.findOne({ name: itemName })
const user = await UserModel.findById(userId)
if (item.name === 'free' && item.price === 0) {
if (user.daily_free_request > 0) {
user.daily_free_request -= 1
await user.save()
const project = await AdvertisingModel.findById(advertisingId)
if (!project) {
res.status(404).send('Project not found')
return
}
project.payment_status = 'done' // Set project status to 'done'
project.status = 'paid' // Set project status to 'done'
await project.save()
res.status(200).json({ message: 'پروژه رایگان با موفقیت ایجاد شد', advertisingId, type: 'free' })
} else {
res.status(403).json({ message: 'شما قبلاً از درخواست رایگان امروز استفاده کرده‌اید.' })
}
} else {
const response = await axios.post('https://www.zarinpal.com/pg/rest/WebGate/PaymentRequest.json', {
MerchantID: apiKey,
Amount: showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100),
Description: `پرداخت برای سفارش شماره ${advertisingId}`,
CallbackURL: `${process.env.APP_URL}/advertising/republish-verify-payment-web?advertisingId=${advertisingId}&itemName=${itemName}&userId=${userId}&showDiscount=${showDiscount}` // Assuming your backend URL for handling payment callback
})
const data = response.data
if (data.Status === 100) {
// Payment request was successful
const authority = data.Authority // The payment authority (شناسه پرداخت)
res.status(200).json({ authority })
} else {
// Payment request failed
console.error('Error occurred while initiating payment:', data)
res.status(500).json({ error: 'Error occurred while initiating payment' })
}
}
} catch (error) {
// Error occurred while getting price from database or sending the request
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
const handleAdvertisingRepublishPaymentCallbackWeb = async (req, res) => {
try {
// console.log(req.query)
// Extract payment status and authority from ZarinPal callback
const status = req.query.Status
const authority = req.query.Authority
const advertisingId = req.query.advertisingId // Get project ID from session
const itemName = req.query.itemName // Get project ID from session
const userId = req.query.userId // Get project ID from session
const showDiscount = req.query.showDiscount // Get project ID from session
// Clear project ID from session
// Query ZarinPal API to verify payment status
const verificationUrl = 'https://www.zarinpal.com/pg/rest/WebGate/PaymentVerification.json'
const price = await getAdvertisingPriceFromDatabase(itemName) // Get price from database
const verificationResponse = await axios.post(verificationUrl, {
MerchantID: apiKey,
Authority: authority,
Amount: showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100)
})
const verificationData = verificationResponse.data
if (verificationData.Status === 100) {
const payment = new PaymentModel({
amount: showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100),
status: 'successful',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
advertising_id: advertisingId,
type: 'advertising',
installment_step: null
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
await payment.save()
// Payment verification is successful
// Now, update project status to 'done' or desired status
const advertising = await AdvertisingModel.findById(advertisingId)
if (!advertising) {
res.status(404).send('Advertising not found')
return
}
advertising.payment_status = 'done' // Set advertising status to 'done'
advertising.status = 'accepted' // Set advertising status to 'done'
advertising.acceptedAt = new Date()
await advertising.save()
// Send event based on payment status
if ('eventEmitter' in req.app) {
if (status === 'OK') {
// Payment is successful
// Emit an event to be listened to in the frontend
// Example event name: 'paymentSuccess'
// Example data: { message: 'Payment successful' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Payment successful' })
} else {
// Payment is canceled or failed
// Emit an event to be listened to in the frontend
// Example event name: 'paymentFailed'
// Example data: { message: 'Payment canceled or failed' }
// You can customize the event name and data as needed
req.app.get('eventEmitter').emit('paymentFailed', { message: 'Payment canceled or failed' })
}
} else {
console.error('Error: eventEmitter is not defined or not properly configured.')
}
// Redirect to the callback URL with the advertising ID
res.redirect(`https://modstagram.com/billboards/payment/success?projectId=${advertisingId}&price=${showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100)}&type=advertising-republish&showDiscount=${showDiscount}`)
} else {
// Payment verification failed
const payment = new PaymentModel({
amount: showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100),
status: 'failed',
authority,
user_id: userId, // فرضاً شما از نوع میان‌افزارهای مدیریت کاربر استفاده می‌کنید و اطلاعات کاربر را از طریق `req.user` در دسترس دارید
advertising_id: advertisingId,
type: 'advertising'
// دیگر اطلاعات مورد نیاز برای ثبت پرداخت
})
await payment.save()
res.redirect(`https://modstagram.com/billboards/payment/failed?projectId=${advertisingId}&price=${showDiscount === true || showDiscount === 'true' ? Number(((price + 20000) * 70) / 100) : Number(((price) * 70) / 100)}&type=advertising-republish&showDiscount=${showDiscount}`)
}
} catch (error) {
// Error occurred while verifying payment
console.error('Error occurred:', error)
res.status(500).send('Internal server error')
}
}
// const initiateOfferPaymentWeb = async (req, res) => {
// try {
// const token = req.header('Authorization')?.split(' ')[1];
// if (!token) {
// return res.status(401).json({ error: 'Access Denied: No token provided' });
// }
// let decodedToken;
// try {
// decodedToken = jwt.verify(token, process.env.APP_SECRET);
// } catch (err) {
// return res.status(401).json({ error: 'Invalid token' });
// }
// const userId = decodedToken.id;
// const { receiverId, offerType } = req.body;
// // اعتبارسنجی ورودی‌ها
// if (!receiverId || !offerType) {
// return res.status(400).json({ error: 'receiverId and offerType are required' });
// }
// const user = await UserModel.findById(userId);
// if (!user) {
// return res.status(404).json({ error: 'User not found' });
// }
// const receiverUser = await UserModel.findById(receiverId);
// if (!receiverUser) {
// return res.status(404).json({ error: 'Receiver not found' });
// }
// let price;
// if (user.monthly_free_offer > 0 && offerType === 'free') {
// price = 0;
// user.monthly_free_offer -= 1;
// await user.save();
// } else {
// price = await getOfferPriceFromDatabase(offerType);
// }
// if (price === 0) {
// const offer = new OfferModel({
// sender: userId,
// receiver: receiverId,
// transaction_id: null,
// });
// await offer.save();
// return res.status(200).json({ message: 'آفر رایگان با موفقیت ثبت شد', offerId: offer._id, type: 'free' });
// }
// console.log('🔹 Initiating ZarinPal Payment');
// console.log('MerchantID:', apiKey);
// console.log('Amount:', Number(price * 10));
// console.log('Description:', `پرداخت برای ارسال آفر به ${receiverUser.first_name}`);
// console.log('CallbackURL:', `${process.env.APP_URL}/offers/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}`);
// const ZARINPAL_URL = 'https://api.zarinpal.com/pg/v4/payment/request.json';
// //
// const response = await axios.post(ZARINPAL_URL, {
// merchant_id: process.env.ZARINPAL_MERCHANT_ID, // ❌ MerchantID نیست!
// amount: Number(price), // ✅ به ریال
// description: `پرداخت برای ارسال آفر به ${receiverUser.first_name}`,
// callback_url: `${process.env.APP_SITE_CallBack}/offer/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}`,
// }, { headers: { 'Content-Type': 'application/json' } });
// const result = response.data.data; // ✅ data.data
// if (result.code === 100) { // ✅ code === 100
// return res.status(200).json({
// authority: result.authority,
// paymentUrl: `https://www.zarinpal.com/pg/StartPay/${result.authority}`
// });
// } else {
// console.error('ZarinPal error:', data);
// return res.status(500).json({ error: `Failed to initiate payment: ZarinPal Status ${data.Status}` });
// }
// } catch (error) {
// console.error('❌ Error in initiateOfferPaymentWeb:', error.message, error.stack);
// return res.status(500).json({ error: error.message || 'Internal server error' });
// }
// }
const initiateOfferPaymentWeb = async (req, res) => {
try {
const token = req.header('Authorization')?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'No token' });
const decoded = jwt.verify(token, process.env.APP_SECRET);
const userId = decoded.id;
const { receiverId, offerType } = req.body;
if (!receiverId || !offerType)
return res.status(400).json({ error: 'Invalid data' });
const user = await UserModel.findById(userId);
const receiver = await UserModel.findById(receiverId);
if (!user || !receiver)
return res.status(404).json({ error: 'User not found' });
let price = await getOfferPriceFromDatabase(offerType); // ✅ ریال
// 🎁 free offer
if (offerType === 'free' && user.monthly_free_offer > 0) {
const offer = await OfferModel.create({
sender: userId,
receiver: receiverId,
transaction_id: null,
});
user.monthly_free_offer -= 1;
await user.save();
return res.json({
type: 'free',
offerId: offer._id,
});
}
const amount = Number(price) * 10; // ✅ ریال بدون ×10
const callbackUrl = `https://api.modstagram.com/api/v1/offers/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}`;
const zarinpalRes = await axios.post(
'https://api.zarinpal.com/pg/v4/payment/request.json',
{
merchant_id:"c7c41e8a-918f-4741-bcd5-58f3bc51db73",
amount,
description: `ارسال آفر به ${receiver.first_name}`,
callback_url: callbackUrl
// callback_url: `${process.env.APP_SITE_CallBack}/offers/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}`,
},
{ headers: { 'Content-Type': 'application/json' } }
);
// console.log(`${process.env.APP_SITE_CallBack}/offers/payment-web?receiverId=${receiverId}&offerType=${offerType}&userId=${userId}`);
const result = zarinpalRes.data.data;
console.log(result);
console.log("ZarinPal Callback URL:", callbackUrl);
console.log("Initiate payment request:", { userId, receiverId, offerType, price });
if (result.code === 100) {
return res.json({
authority: result.authority,
paymentUrl: `https://www.zarinpal.com/pg/StartPay/${result.authority}`,
});
}
return res.status(500).json({ error: 'ZarinPal error' });
} catch (err) {
console.error(err);
return res.status(500).json({ error: 'Server error' });
}
};
// try {
// const { receiverId, offerType, userId, Authority, Status } = req.query;
// // ❗ چک اولیه
// if (Status !== 'OK' || !Authority) {
// return res.status(400).json({
// success: false,
// message: 'پرداخت ناموفق یا لغو شد'
// });
// }
// if (!receiverId || !offerType || !userId) {
// return res.status(400).json({
// success: false,
// message: 'پارامترهای ناقص'
// });
// }
// // پیدا کردن کاربرها
// const user = await UserModel.findById(userId);
// const receiverUser = await UserModel.findById(receiverId);
// if (!user || !receiverUser) {
// return res.status(404).json({
// success: false,
// message: 'کاربر یافت نشد'
// });
// }
// const price = await getOfferPriceFromDatabase(offerType);
// const verifyResponse = await axios.post(
// 'https://api.zarinpal.com/pg/v4/payment/verify.json',
// {
// merchant_id: process.env.ZARINPAL_MERCHANT_ID, // یا MERCHENT_CODE
// authority: Authority,
// amount: price, // به ریال
// },
// { headers: { 'Content-Type': 'application/json' } }
// );
// const verifyData = verifyResponse.data.data;
// if (verifyData.code === 100) {
// // ✅ پرداخت موفق
// const payment = new PaymentModel({
// amount: Number(price),
// status: 'successful',
// authority: Authority,
// ref_id: verifyData.ref_id,
// user_id: userId,
// type: 'offer',
// });
// await payment.save();
// const offer = new OfferModel({
// sender: userId,
// receiver: receiverId,
// transaction_id: payment._id,
// });
// await offer.save();
// // نوتیفیکیشن و SMS (همون کد قبلی)
// const notification = new NotificationModel({
// user_id: receiverId,
// project_post_id: offer._id,
// type: 'new_offer',
// title: 'درخواست همکاری جدید',
// description: `${user.first_name} یک درخواست همکاری برای شما ارسال کرده است`,
// });
// await notification.save();
// const data = JSON.stringify({
// mobile: receiverUser?.mobile,
// templateId: '569006',
// parameters: [
// { name: 'FIRSTNAME', value: receiverUser?.first_name },
// { name: 'LASTNAME', value: receiverUser?.last_name },
// ],
// });
// const config = {
// method: 'post',
// url: 'https://api.sms.ir/v1/send/verify',
// headers: {
// 'Content-Type': 'application/json',
// Accept: 'text/plain',
// 'x-api-key': process.env.SMS_VERIFY_KEY,
// },
// data,
// };
// await axios(config).catch((error) => {
// console.error('SMS error:', error.message);
// });
// if ('eventEmitter' in req.app && status === 'OK') {
// req.app.get('eventEmitter').emit('paymentSuccess', { message: 'Payment successful' });
// } else {
// req.app.get('eventEmitter')?.emit('paymentFailed', { message: 'Payment canceled or failed' });
// }
// res.redirect(`https://modstagram.com/offer/payment/success?userId=${userId}`);
// return res.status(200).json({
// success: true,
// message: 'پرداخت و آفر با موفقیت ثبت شد',
// ref_id: verifyData.ref_id,
// offerId: offer._id
// });
// } else {
// // پرداخت ناموفق
// const payment = new PaymentModel({
// amount: Number(price),
// status: 'failed',
// authority: Authority,
// user_id: userId,
// type: 'offer',
// });
// await payment.save();
// res.redirect(`https://modstagram.com/offer/payment/failed?userId=${userId}`);
// return res.status(400).json({
// success: false,
// message: `خطا در تأیید پرداخت: ${verifyData.code}`
// });
// }
// } catch (error) {
// console.error('❌ Callback Error:', error.message);
// return res.status(500).json({
// success: false,
// message: 'خطای سرور'
// });
// }
// };
const handleOfferPaymentCallback = async (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
console.log('🔔 CALLBACK QUERY:', req.query);
try {
const { Authority, Status, receiverId, offerType, userId } = req.query;
if (Status !== 'OK')
return res.redirect(`${process.env.APP_SITE}/offer/payment/failed`);
const price = await getOfferPriceFromDatabase(offerType); // ریال
const amount = Number(price) * 10;
const verify = await axios.post(
'https://api.zarinpal.com/pg/v4/payment/verify.json',
{
merchant_id: "c7c41e8a-918f-4741-bcd5-58f3bc51db73",
authority: Authority,
amount,
},
{ headers: { 'Content-Type': 'application/json' } }
);
const data = verify.data.data;
console.log('🔔payment:', data.code);
if (data.code === 100) {
const payment = await PaymentModel.create({
amount : (amount / 10),
status: 'successful',
authority: Authority,
ref_id: data.ref_id,
user_id: userId,
type: 'offer',
});
console.log('🔔payment:', payment);
const offer = await OfferModel.create({
sender: userId,
receiver: receiverId,
transaction_id: payment._id,
});
return res.redirect(
`${process.env.APP_SITE}/offer/payment/success?offerId=${offer._id}`
);
}
return res.redirect(`${process.env.APP_SITE}/offer/payment/failed`);
} catch (err) {
console.error(err);
return res.redirect(`${process.env.APP_SITE}/offer/payment/failed`);
}
};
module.exports = {
initiatePaymentWeb,
handlePaymentCallbackWeb,
paymentRequestAcceptWeb,
initiateAdvertisingPaymentWeb,
handleAdvertisingPaymentCallbackWeb,
republishAdvertisingPaymentWeb,
handleAdvertisingRepublishPaymentCallbackWeb,
initiateOfferPaymentWeb,
handleOfferPaymentCallback,
};

View File

@@ -1,64 +0,0 @@
/* eslint-disable camelcase */
const PostModel = require('../../../models/PostModel');
const jwt = require('jsonwebtoken');
const UserModel = require('../../../models/UserModel');
const LikeModel = require('../../../models/LikeModel');
const toggleLike = async (req, res, next) => {
try {
const { postId } = req.body;
const token = req.header('Authorization')?.split(' ')[1];
if (!token) {
return res.status(401).json({ message: 'توکن ارائه نشده است' });
}
let userId;
try {
const decodedToken = jwt.verify(token, process.env.APP_SECRET);
userId = decodedToken.id;
} catch (err) {
return res.status(401).json({ message: 'توکن نامعتبر است' });
}
if (!postId) {
return res.status(400).json({ message: 'شناسه پست الزامی است' });
}
const post = await PostModel.findById(postId);
if (!post) {
return res.status(404).json({ message: 'پست مورد نظر یافت نشد' });
}
const isLiked = post.likes.includes(userId);
if (isLiked) {
// unlike
post.likes.pull(userId);
await post.save();
await LikeModel.findOneAndDelete({ postId, userId });
return res.status(200).json({
message: 'لایک با موفقیت حذف شد',
is_liked: false,
likesCount: post.likes.length,
});
} else {
// like
post.likes.push(userId);
await post.save();
await LikeModel.create({ postId, userId });
return res.status(200).json({
message: 'پست با موفقیت لایک شد',
is_liked: true,
likesCount: post.likes.length,
});
}
} catch (error) {
console.error('خطا در toggleLike:', error);
res.status(500).json({ message: 'خطا در سرور' });
next(error);
}
};
module.exports = { toggleLike };

View File

@@ -1,182 +0,0 @@
/* eslint-disable camelcase */
const PostModel = require('../../../models/PostModel');
const UserModel = require('../../../models/UserModel');
const { check, validationResult } = require('express-validator');
const jwt = require('jsonwebtoken');
const path = require('path');
const { default: mongoose } = require('mongoose');
const createPostValidationRules = () => {
console.log(4);
return [
check('caption')
.notEmpty().withMessage('توضیحات نمی‌تواند خالی باشد'),
];
};
const createPost = async (req, res) => {
try {
console.log('DEBUG: createPost called');
console.log('DEBUG: req.files:', req.files);
console.log('DEBUG: req.body:', req.body);
const { caption } = req.body;
const files = req.files || [];
const userId = req.user.id; // از میدل‌ویر auth
if (!files || files.length === 0) {
console.log('DEBUG: No files provided');
return res.status(400).json({ error: true, message: 'هیچ فایلی آپلود نشده است' });
}
if (!caption) {
console.log('DEBUG: No caption provided');
return res.status(400).json({ error: true, message: 'کپشن الزامی است' });
}
const filePaths = files.map((file) => ({
path: file.path,
type: file.mimetype.startsWith('video/') ? 'video' : 'image',
}));
console.log('DEBUG: Saving post to MongoDB, filePaths:', filePaths);
const post = new PostModel({
user_id: userId,
caption,
files: filePaths,
type: files[0].mimetype.startsWith('video/') ? 'video' : 'image', // اضافه کردن فیلد type
createdAt: new Date(),
});
await post.save();
console.log('DEBUG: Post saved:', post._id);
res.status(201).json({ message: 'پست با موفقیت ایجاد شد', postId: post._id });
} catch (err) {
console.error('DEBUG: createPost error:', err.message);
res.status(500).json({ error: true, message: `خطا در سرور: ${err.message}` });
}
};
const getUserPosts = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const user_id = req.query.user_id
// eslint-disable-next-line no-unused-vars
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userReqId = decodedToken.id
if (!mongoose.Types.ObjectId.isValid(user_id)) {
return res.status(400).json({ message: 'شناسه کاربر معتبر نیست' })
}
const user = await UserModel.findById(user_id)
if (!user) {
return res.status(404).json({ message: 'کاربر مورد نظر یافت نشد' })
}
const response = {}
const options = {
page: req.query.page || 1, // صفحه پیش‌فرض ۱
limit: req.query.limit || 10, // حداکثر تعداد موارد برای هر صفحه
sort: { createdAt: -1 }
}
if (user_id.toString() === userReqId.toString()) {
// کاربر خودش است، همه پست‌هایش را بررسی می‌کند
const posts = await PostModel.paginate(
{ user_id: user._id, status: { $in: ['pending', 'accept'] } }, // فیلتر
options // گزینه‌های پیجینیشن
)
response.posts = posts.docs
response.postsCount = posts.totalDocs
response.totalPages = posts.totalPages // ارسال تعداد کل صفحات
response.totalItems = posts.totalDocs
} else {
// دیگران، فقط پست‌هایی که وضعیتشان "accept" است را ببینند
const posts = await PostModel.paginate(
{ user_id: user._id, status: 'accept' }, // فیلتر
options // گزینه‌های پیجینیشن
)
response.posts = posts.docs
response.postsCount = posts.totalDocs
response.totalPages = posts.totalPages // ارسال تعداد کل صفحات
response.totalItems = posts.totalDocs
}
return res.status(200).json({ data: response })
} catch (error) {
next(error)
}
}
const getUserPostsWeb = async (req, res, next) => {
try {
let userReqId = null
// دریافت توکن و بررسی معتبر بودنش (اگر وجود داشت)
const authHeader = req.header('Authorization')
if (authHeader) {
const token = authHeader.split(' ')[1]
try {
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
userReqId = decodedToken.id
} catch (err) {
console.warn('توکن نامعتبر است، ادامه بدون احراز هویت') // فقط هشدار، نه ارور
}
}
const user_id = req.query.user_id
if (!mongoose.Types.ObjectId.isValid(user_id)) {
return res.status(400).json({ message: 'شناسه کاربر معتبر نیست' })
}
const user = await UserModel.findById(user_id)
if (!user) {
return res.status(404).json({ message: 'کاربر مورد نظر یافت نشد' })
}
const data = {}
const options = {
page: req.query.page || 1,
limit: req.query.limit || 10,
sort: { createdAt: -1 }
}
if (userReqId && user_id.toString() === userReqId.toString()) {
// اگر کاربر لاگین کرده و درخواست برای خودش است، همه پست‌هایش را ببیند
const posts = await PostModel.paginate(
{ user_id: user._id, status: { $in: ['pending', 'accept'] } },
options
)
data.posts = posts.docs
data.postsCount = posts.totalDocs
data.totalPages = posts.totalPages
data.totalItems = posts.totalDocs
} else {
// برای بقیه، فقط پست‌هایی که وضعیتشان "accept" است نمایش داده شود
const posts = await PostModel.paginate(
{ user_id: user._id, status: 'accept' },
options
)
data.posts = posts.docs
data.postsCount = posts.totalDocs
data.totalPages = posts.totalPages
data.totalItems = posts.totalDocs
}
return res.status(200).json(data)
} catch (error) {
next(error)
}
}
module.exports = {
createPostValidationRules,
createPost,
getUserPosts,
getUserPostsWeb
}

View File

@@ -1,139 +0,0 @@
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
// GET user by ID from URL
const getUserById = async (req, res, next) => {
try {
const userId = req.params.id; // گرفتن آیدی از پارامتر URL
const service = req.query.services;
const user = await UserModel.findById(userId);
if (!user) {
return res.status(404).json({ message: "کاربر مورد نظر یافت نشد" });
}
let isRegister = true;
// if (
// !user.user_name ||
// !user.first_name ||
// !user.last_name ||
// !user.user_type ||
// !user.national_card_image ||
// !user.password ||
// !user.national_code
// ) {
// isRegister = false;
// }
const response = {
_id: user._id,
user_type: user.user_type,
user_level: user.user_level,
first_name: user.first_name,
last_name: user.last_name,
user_name: user.user_name,
show_location: user.show_location,
is_verified: user.is_verified,
user_score: user.user_score,
rate: user.rate,
bio: user.bio,
height: user.height,
weight: user.weight,
size: user.size,
eye_color: user.eye_color,
hair_color: user.hair_color,
profile_image: user.profile_image,
lat: user.lat,
lng: user.lng,
address: user.address,
province: user.province,
city: user.city,
shaba: user.shaba,
national_code: user.national_code,
mobile: user.mobile,
cooperation_abroad: user.cooperation_abroad,
cooperation_type: user.cooperation_type,
conversation_projects: user.conversation_projects,
expertise: user.expertise,
sub_expertise: user.sub_expertise,
isRegister,
user,
services: service === "1" ? user.services : null
};
return res.status(200).json({ user: response });
} catch (error) {
next(error);
}
};
const getProfile = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const service = req.query.services
// eslint-disable-next-line no-unused-vars
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
const user = await UserModel.findById(userId)
let isRegister = true
if (!user.user_name ||
!user.first_name ||
!user.last_name ||
!user.user_type ||
!user.national_card_image ||
!user.password ||
!user.national_code
) {
isRegister = false
}
if (!user) {
return res.status(404).json({ message: 'کاربر مورد نظر یافت نشد' })
}
const response = {
_id: user._id,
user_type: user.user_type,
user_level: user.user_level,
first_name: user.first_name,
last_name: user.last_name,
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,
height: user.height,
weight: user.weight,
size: user.size,
eye_color: user.eye_color,
hair_color: user.hair_color,
profile_image: user.profile_image,
lat: user.lat,
lng: user.lng,
address: user.address,
province: user.province,
city: user.city,
shaba: user.shaba,
national_code: user.national_code,
mobile: user.mobile,
cooperation_abroad: user.cooperation_abroad,
cooperation_type: user.cooperation_type,
conversation_projects: user.conversation_projects,
expertise: user.expertise,
sub_expertise: user.sub_expertise,
isRegister,
services: service === '1' ? user.services : null
}
return res.status(200).json({ user: response })
} catch (error) {
next(error)
}
}
module.exports = {
getProfile,
getUserById
};

View File

@@ -1,220 +0,0 @@
/* 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 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 (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 {
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,406 +0,0 @@
/* eslint-disable camelcase */
const { default: mongoose } = require('mongoose')
const ProjectModel = require('../../../models/ProjectModel')
const RequestModel = require('../../../models/RequestModel')
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
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 getProjects = async (req, res, next) => {
try {
// eslint-disable-next-line no-unused-vars
const { expertise, page = 1, limit = 10, most_price, most_requests, age, gender } = req.query
// ساخت فیلتر برای استفاده در جستجوی MongoDB
const filter = {
payment_status: 'done',
status: 'accepted',
public_status: 'public',
$or: [
{ isExpired: false }, // پروژه‌هایی که isExpired برابر با false است
{ isExpired: { $exists: false } } // پروژه‌هایی که فیلد isExpired وجود ندارد
]
}
if (expertise) {
filter.expertise = expertise
}
if (age) {
filter.age = age
}
if (gender) {
filter.gender = gender
}
// استفاده از مدل پیجینیت شده برای دریافت نتایج صفحه‌بندی شده
if (most_price || most_requests) {
// دریافت لیست پروژه‌ها با استفاده از فیلتر و گزینه‌های صفحه‌بندی
const options = {
page: parseInt(page), // تبدیل صفحه به عدد صحیح
limit: parseInt(limit), // تبدیل محدودیت به عدد صحیح
sort: most_price === 'true' ? { offer_price: -1 } : { requested_users: 'desc' }
}
const projects = await ProjectModel.paginate(filter, options)
const newProj = projects.docs
res.status(200).json({
projects: newProj,
totalPages: projects.totalPages, // ارسال تعداد کل صفحات
totalItems: projects.totalDocs // ارسال تعداد کل آیتم‌ها
})
} else {
// دریافت لیست پروژه‌ها با استفاده از فیلتر و گزینه‌های صفحه‌بندی
const options = {
page: parseInt(page), // تبدیل صفحه به عدد صحیح
limit: parseInt(limit), // تبدیل محدودیت به عدد صحیح
sort: { acceptedAt: -1, createdAt: -1 } // مرتب‌سازی بر اساس acceptedAt و سپس createdAt
}
const projects = await ProjectModel.paginate(filter, options)
const newProj = projects.docs
res.status(200).json({
projects: newProj,
totalPages: projects.totalPages, // ارسال تعداد کل صفحات
totalItems: projects.totalDocs // ارسال تعداد کل آیتم‌ها
})
}
} catch (error) {
next(error)
}
// try {
// const token = req.header('Authorization').split(' ')[1]
// if (!token) return res.status(401).send('Access Denied')
// // eslint-disable-next-line no-unused-vars
// const decodedToken = jwt.verify(token, process.env.APP_SECRET)
// const { expertise, page = 1, limit = 10 } = req.query // افزودن پارامترهای صفحه‌بندی به درخواست
// // ساخت فیلتر برای استفاده در جستجوی MongoDB
// const filter = {
// payment_status: 'done',
// status: 'accepted',
// public_status: 'public'
// } // افزودن شرط‌های دیگر برای فیلتر
// if (expertise) {
// filter.expertise = expertise
// }
// // استفاده از مدل پیجینیت شده برای دریافت نتایج صفحه‌بندی شده
// const options = {
// page: parseInt(page), // تبدیل صفحه به عدد صحیح
// limit: parseInt(limit) // تبدیل محدودیت به عدد صحیح
// }
// // دریافت لیست پروژه‌ها با استفاده از فیلتر و گزینه‌های صفحه‌بندی
// const projects = await ProjectModel.paginate(filter, options)
// // تبدیل نتایج به فرمت مورد نیاز
// const projectsWithUserInfo = await Promise.all(projects.docs.map(async project => {
// const creator = await UserModel.findById(project.creator_id).select('first_name last_name user_name user_type is_verified profile_image gender province city rate')
// const projectWithUserInfo = {
// ...project.toJSON(), // تبدیل آبجکت پروژه به JSON
// creator // اضافه کردن اطلاعات کاربر به آبجکت پروژه
// }
// // محاسبه مدت زمان باقی‌مانده برای هر پروژه
// const currentTime = new Date()
// const projectCreatedAt = new Date(project.createdAt)
// const timeDiff = project.offer_time * 24 * 60 * 60 * 1000 - (currentTime - projectCreatedAt)
// const remainingTime = calculateRemainingTime(timeDiff)
// projectWithUserInfo.remainingTime = remainingTime
// return projectWithUserInfo
// }))
// res.status(200).json({
// projects: projectsWithUserInfo,
// totalPages: projects.totalPages, // ارسال تعداد کل صفحات
// totalItems: projects.totalDocs // ارسال تعداد کل آیتم‌ها
// })
// } catch (error) {
// next(error)
// }
}
const getUserProjects = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const user_id = req.query.user_id
// eslint-disable-next-line no-unused-vars
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
// const userReqId = decodedToken.id
if (!mongoose.Types.ObjectId.isValid(user_id)) {
return res.status(400).json({ message: 'شناسه کاربر معتبر نیست' })
}
const user = await UserModel.findById(user_id)
if (!user) {
return res.status(404).json({ message: 'کاربر مورد نظر یافت نشد' })
}
const response = {}
if (user.user_type === 'user') {
const options = {
page: req.query.page || 1, // صفحه پیش‌فرض ۱
limit: req.query.limit || 10, // حداکثر تعداد موارد برای هر صفحه
sort: { createdAt: -1 }
}
// پیجینیشن بر روی لیست پروژه‌های کاربر اعمال می‌شود
const projects = await ProjectModel.paginate(
{ selected_user: user._id, status: 'done' }, // فیلتر
options // گزینه‌های پیجینیشن
)
response.projects = projects.docs
response.totalPages = projects.totalPages // ارسال تعداد کل صفحات
response.totalItems = projects.totalDocs // ارسال تعداد کل آیتم‌ها
response.allProjectsCount = await ProjectModel.countDocuments({ selected_user: user._id })
const completedProjects = await Promise.all([
ProjectModel.countDocuments({ selected_user: user._id, status: 'done' })
])
response.successfulProjects = completedProjects
} else {
const options = {
page: req.query.page || 1, // صفحه پیش‌فرض ۱
limit: req.query.limit || 10, // حداکثر تعداد موارد برای هر صفحه
sort: { createdAt: -1 }
}
// پیجینیشن بر روی لیست پروژه‌های کاربر اعمال می‌شود
const projects = await ProjectModel.paginate(
{ creator_id: user._id, status: { $in: ['done', 'accepted'] } }, // فیلتر
options // گزینه‌های پیجینیشن
)
response.projects = projects.docs
response.totalPages = projects.totalPages // ارسال تعداد کل صفحات
response.totalItems = projects.totalDocs
response.allProjectsCount = await ProjectModel.countDocuments({ creator_id: user._id, payment_status: 'done' })
// تعداد پروژه‌های موفق را به دست آورید
const successfulProjects = await ProjectModel.countDocuments({ creator_id: user._id, status: 'done' })
response.successfulProjects = successfulProjects
}
return res.status(200).json({ data: response })
} catch (error) {
next(error)
}
}
const getUserProjectsWeb = async (req, res, next) => {
try {
const user_id = req.query.user_id
// eslint-disable-next-line no-unused-vars
if (!mongoose.Types.ObjectId.isValid(user_id)) {
return res.status(400).json({ message: 'شناسه کاربر معتبر نیست' })
}
const user = await UserModel.findById(user_id)
if (!user) {
return res.status(404).json({ message: 'کاربر مورد نظر یافت نشد' })
}
const data = {}
if (user.user_type === 'user') {
const options = {
page: req.query.page || 1, // صفحه پیش‌فرض ۱
limit: req.query.limit || 10, // حداکثر تعداد موارد برای هر صفحه
sort: { createdAt: -1 }
}
// پیجینیشن بر روی لیست پروژه‌های کاربر اعمال می‌شود
const projects = await ProjectModel.paginate(
{ selected_user: user._id, status: 'done' }, // فیلتر
options // گزینه‌های پیجینیشن
)
data.projects = projects.docs
data.totalPages = projects.totalPages // ارسال تعداد کل صفحات
data.totalItems = projects.totalDocs // ارسال تعداد کل آیتم‌ها
data.allProjectsCount = await ProjectModel.countDocuments({ selected_user: user._id })
const completedProjects = await Promise.all([
ProjectModel.countDocuments({ selected_user: user._id, status: 'done' })
])
data.successfulProjects = completedProjects
} else {
const options = {
page: req.query.page || 1, // صفحه پیش‌فرض ۱
limit: req.query.limit || 10, // حداکثر تعداد موارد برای هر صفحه
sort: { createdAt: -1 }
}
// پیجینیشن بر روی لیست پروژه‌های کاربر اعمال می‌شود
const projects = await ProjectModel.paginate(
{ creator_id: user._id, status: { $in: ['done', 'accepted'] } }, // فیلتر
options // گزینه‌های پیجینیشن
)
data.projects = projects.docs
data.totalPages = projects.totalPages // ارسال تعداد کل صفحات
data.totalItems = projects.totalDocs
data.allProjectsCount = await ProjectModel.countDocuments({ creator_id: user._id, payment_status: 'done' })
// تعداد پروژه‌های موفق را به دست آورید
const successfulProjects = await ProjectModel.countDocuments({ creator_id: user._id, status: 'done' })
data.successfulProjects = successfulProjects
}
return res.status(200).json(data)
} catch (error) {
next(error)
}
}
const getSingleProjects = 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 projectId = req.params.projectId
const projectRequests = await RequestModel.find({ project: projectId })
.populate('user', 'price time user_level first_name last_name user_name is_verified user_score rate profile_image')
.sort({ status: 1 })
.lean()
const modifiedRequests = projectRequests.map(request => ({
...request,
time: request.user._id.toString() === userId ? request.time : null,
price: request.user._id.toString() === userId ? request.price : null
}))
const project = await ProjectModel.findById(projectId)
if (!project) {
return res.status(404).json({ message: 'پروژه مورد نظر یافت نشد' })
}
// محاسبه مبلغ باقیمانده و مجموع مبلغ پرداخت شده
// تابع محاسبه مبلغ پرداخت شده
const getTotalPaidAmount = (installments) => {
let totalPaidAmount = 0
installments.forEach(installment => {
totalPaidAmount += installment.amount
})
return totalPaidAmount
}
// تابع محاسبه مبلغ باقیمانده
const getRemainingAmount = (project) => {
const totalPaidAmount = getTotalPaidAmount(project.installments)
const remainingAmount = project.final_price - totalPaidAmount
return remainingAmount
}
const totalPaidAmount = getTotalPaidAmount(project.installments)
const remainingAmount = getRemainingAmount(project)
// اطلاعات کاربر سازنده را نیز دریافت کنید و به آبجکت پروژه اضافه کنید
const creator = await UserModel.findById(project.creator_id).select('first_name last_name user_name user_type is_verified profile_image gender province city rate')
const projectWithUserInfo = {
...project._doc,
creator // اضافه کردن اطلاعات کاربر به آبجکت پروژه
}
const selectedUser = await UserModel.findById(project.selected_user).select('_id user_name first_name last_name user_type is_verified profile_image expertise sub_expertise province city rate user_level user_score')
projectWithUserInfo.selected_user = selectedUser
// محاسبه مدت زمان باقی‌مانده برای پروژه
const currentTime = new Date()
const projectCreatedAt = new Date(project.createdAt)
const timeDiff = project.offer_time * 24 * 60 * 60 * 1000 - (currentTime - projectCreatedAt)
const remainingTime = calculateRemainingTime(timeDiff)
projectWithUserInfo.remainingTime = remainingTime
let projectDetails = projectWithUserInfo
if ((project.selected_user && project.selected_user.toString()) === userId || project.creator_id.toString() === userId) {
projectDetails = {
...projectWithUserInfo,
total_paid_amount: totalPaidAmount,
remaining_amount: remainingAmount
}
} else {
projectDetails =
projectWithUserInfo
}
// }
res.status(200).json({ project: projectDetails, projectRequests: modifiedRequests })
} catch (error) {
next(error)
}
}
const getSingleProjectsWeb = async (req, res, next) => {
try {
let userId = null
let token = req.header('Authorization')
if (token) {
try {
token = token.split(' ')[1]
if (token) {
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
userId = decodedToken.id
}
} catch (err) {
console.warn('Invalid token:', err.message)
}
}
const projectId = req.params.projectId
const projectRequests = await RequestModel.find({ project: projectId })
.populate('user', 'price time user_level first_name last_name user_name is_verified user_score rate profile_image')
.sort({ status: 1 })
.lean()
// اگر کاربر لاگین کرده باشد، زمان و قیمت را نشان بده، در غیر این صورت حذف کن
const modifiedRequests = projectRequests.map(request => ({
...request,
time: userId && request.user._id.toString() === userId ? request.time : null,
price: userId && request.user._id.toString() === userId ? request.price : null
}))
const project = await ProjectModel.findById(projectId)
if (!project) {
return res.status(404).json({ message: 'پروژه مورد نظر یافت نشد' })
}
// محاسبه مبلغ باقیمانده و مجموع مبلغ پرداخت شده
const getTotalPaidAmount = (installments) => {
return installments.reduce((total, installment) => total + installment.amount, 0)
}
const getRemainingAmount = (project) => {
return project.final_price - getTotalPaidAmount(project.installments)
}
const totalPaidAmount = getTotalPaidAmount(project.installments)
const remainingAmount = getRemainingAmount(project)
// دریافت اطلاعات سازنده پروژه
const creator = await UserModel.findById(project.creator_id).select('first_name last_name user_name user_type is_verified profile_image gender province city rate')
const projectWithUserInfo = {
...project._doc,
creator
}
// دریافت اطلاعات کاربر منتخب در صورت وجود
const selectedUser = await UserModel.findById(project.selected_user).select('_id user_name first_name last_name user_type is_verified profile_image expertise sub_expertise province city rate user_level user_score')
projectWithUserInfo.selected_user = selectedUser
// محاسبه زمان باقی‌مانده برای پیشنهادات پروژه
const currentTime = new Date()
const projectCreatedAt = new Date(project.createdAt)
const timeDiff = project.offer_time * 24 * 60 * 60 * 1000 - (currentTime - projectCreatedAt)
projectWithUserInfo.remainingTime = calculateRemainingTime(timeDiff)
let projectDetails = projectWithUserInfo
// فقط اگر کاربر احراز هویت شده باشد و سازنده یا برنده پروژه باشد، اطلاعات مالی را برگردان
if (userId && (project.selected_user?.toString() === userId || project.creator_id.toString() === userId)) {
projectDetails = {
...projectWithUserInfo,
total_paid_amount: totalPaidAmount,
remaining_amount: remainingAmount
}
}
res.status(200).json({ project: projectDetails, projectRequests: modifiedRequests })
} catch (error) {
next(error)
}
}
// const projectDetails = await ProjectModel.findById(projectId).populate('requested_users', 'price time user_level first_name last_name user_name is_verified user_score rate profile_image') // جزییات درخواست‌های کاربران
module.exports = {
getProjects, getSingleProjects, getUserProjects, getUserProjectsWeb, getSingleProjectsWeb
}

View File

@@ -1,41 +0,0 @@
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,195 +0,0 @@
/* 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 || user.user_type !== '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: 'شما قبلاً برای این پروژه درخواست داده‌اید'
})
}
// ایجاد یک درخواست جدید
const request = new RequestModel({
project: project_id,
user: user._id,
time: request_time,
price: offer_price
})
await request.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()
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 || user.user_type !== '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,381 +0,0 @@
/* 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 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 || !rate || !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 newComment = new CommentModel({
user: user_id,
project: project_id,
creator: userId,
rating: rate,
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 = 'استاد'
}
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 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 || !rate || !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 newComment = new CommentModel({
user: user_id,
project: project_id,
creator: userId,
rating: rate,
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 = 'استاد'
}
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 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 || !rate || !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 newComment = new CommentModel({
user: user_id,
project: project_id,
creator: userId,
rating: rate,
comment,
comment_for: 'project',
status: 'pending'
})
await newComment.save()
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 (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 {
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.' })
}
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
}

View File

@@ -1,15 +0,0 @@
const { ProvinceModel } = require('../../models/StateCity')
const getProvinces = async (req, res, next) => {
try {
const provinces = await ProvinceModel.find({})
res.status(200).json({
provinces
})
} catch (err) {
res.status(500).send('Internal Server Error')
}
}
module.exports = {
getProvinces
}

View File

@@ -1,89 +0,0 @@
const { default: axios } = require('axios')
const UserModel = require('../../../models/UserModel')
const { check, validationResult } = require('express-validator')
// const { Token, VerificationCode } = require('sms-ir')
const registerValidationRules = () => {
return [
check('mobile')
.notEmpty().withMessage('شماره موبایل نمی‌تواند خالی باشد')
.isLength({ min: 11, max: 11 }).withMessage('شماره موبایل باید دقیقاً 11 رقم باشد')
.matches(/^09[0-9]{9}$/).withMessage('فرمت شماره موبایل صحیح نیست')
]
}
const registerUser = async (req, res, next) => {
try {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const { mobile } = req.body
if (!mobile) {
return res.status(422).json({
error: true,
message: 'شماره موبایل نمی‌تواند خالی باشد'
})
}
const existingUser = await UserModel.findOne({ mobile })
if (existingUser && existingUser.national_code !== null) {
return res.status(422).json({ error: true, message: 'شماره موبایل تکراری است' })
}
function generateOTP () {
return Math.floor(100000 + Math.random() * 900000)
}
const otp = generateOTP()
// eslint-disable-next-line no-unused-vars
const user = await UserModel.findOneAndUpdate(
{ mobile },
{ $set: { mobile, otp } },
{ upsert: true, new: true, lean: true }
)
setTimeout(() => {
UserModel.findOneAndUpdate({ mobile }, { $set: { otp: null } }, { new: true })
.then(() => {})
.catch(error => console.error('Error setting OTP to null:', error))
}, 5 * 60 * 1000)
const data = JSON.stringify({
mobile,
templateId: '930719',
parameters: [
{ name: 'CODE', value: otp.toString() }
]
})
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) {
// console.log(JSON.stringify(response.data))
})
.catch(function (error) {
console.log(error)
})
res.status(200).json({
success: true,
message: 'کد تایید ارسال شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
registerValidationRules,
registerUser
}

View File

@@ -1,51 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const { check, validationResult } = require('express-validator')
const jwt = require('jsonwebtoken')
const setUserFullNameValidationRules = () => {
return [
check('first_name')
.notEmpty().withMessage('نام نمی‌تواند خالی باشد'),
check('last_name')
.notEmpty().withMessage('نام خانوادگی نمی‌تواند خالی باشد')
]
}
const setUserFullName = async (req, res, next) => {
try {
const { first_name, last_name } = req.body
// اعتبارسنجی داده‌های ورودی
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
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: 'کاربری با این شماره موبایل یافت نشد'
})
}
// آپدیت نام کاربری به صورت lowercase
user.first_name = first_name.toLowerCase()
user.last_name = last_name.toLowerCase()
await user.save()
return res.json({
message: 'نام و نام خانوادگی با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
setUserFullName, setUserFullNameValidationRules
}

View File

@@ -1,44 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setUserType = async (req, res, next) => {
try {
const { user_type } = req.body
if (!user_type || !['user', 'employer'].includes(user_type)) {
return res.status(422).json({
error: true,
message: 'نوع یوزر معتبر نیست'
})
}
// جستجوی کاربر با شماره موبایل ارسالی
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.user_type = user_type.toLowerCase()
await user.save()
return res.json({
message: 'نوع کاربر با موفقیت به‌روزرسانی شد',
id: user._id
})
} catch (error) {
next(error)
}
}
module.exports = {
setUserType
}

View File

@@ -1,95 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setUserName = async (req, res, next) => {
try {
const { mobile, user_name } = req.body
if (!mobile || !user_name) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// جستجوی کاربر با شماره موبایل ارسالی
const user = await UserModel.findOne({ mobile })
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
// بررسی حداقل طول نام کاربری
if (user_name.length < 5) {
return res.status(422).json({
error: true,
message: 'نام کاربری باید حداقل 5 کاراکتر باشد'
})
}
// بررسی محدودیت‌های مجاز در نام کاربری
// const usernameRegex = /^[a-zA-Z0-9_]+$/
// if (!usernameRegex.test(user_name)) {
// return res.status(422).json({
// error: true,
// message: 'نام کاربری فقط می‌تواند شامل حروف الفبای انگلیسی، اعداد و کاراکتر _ باشد'
// })
// }
// بررسی تکراری بودن user_name
const existingUser = await UserModel.findOne({ user_name: { $regex: new RegExp('^' + user_name + '$', 'i') } })
if (existingUser && existingUser._id.toString() !== user._id.toString()) {
return res.status(422).json({
error: true,
message: 'نام کاربری تکراری است'
})
}
// آپدیت نام کاربری به صورت lowercase
user.user_name = user_name.toLowerCase()
await user.save()
return res.json({
message: 'نام کاربری با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
const updateUserName = 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_name } = req.body
if (!user_name) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const user = await UserModel.findByIdAndUpdate(userId, { user_name }, { new: true })
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شناسه یافت نشد'
})
}
return res.json({
message: 'نام کاربری با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
setUserName, updateUserName
}

View File

@@ -1,47 +0,0 @@
const UserModel = require('../../../models/UserModel')
const TokenService = require('../../../services/TokenService')
// const jwt = require('jsonwebtoken')
const verifyUser = async (req, res, next) => {
try {
const { mobile, otp } = req.body
if (!mobile || !otp) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const user = await UserModel.findOne({ mobile })
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
const token = TokenService.sign({ id: user._id })
let step = ''
if (user.user_name === null) { step = 'user_name' } else if
(!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.otp === otp) {
return res.json({
message: 'کد تایید صحیح بود',
token,
step
})
}
return res.status(422).json({
error: true,
message: 'کد تایید اشتباه است'
})
} catch (error) {
next(error)
}
}
module.exports = {
verifyUser
}

View File

@@ -1,52 +0,0 @@
const ProjectModel = require('../../../models/ProjectModel')
const UserModel = require('../../../models/UserModel')
const getSearch = async (req, res, next) => {
try {
const { search, type, page = 1, limit = 10 } = req.query
let result
const selectedFields = '_id user_name first_name last_name user_type is_verified profile_image expertise rate user_level user_score'
let filter
let totalPages
let totalItems
if (search) {
if (!type) {
res.status(422).json({ success: false, message: 'نوع جستجو را مشخص کنید' })
} else if (type === 'project') {
// جستجو در مدل Project با فیلتر کردن برای user_type === "employer"
filter = { $or: [{ title: new RegExp(search, 'i'), status: 'accepted' }, { description: new RegExp(search, 'i'), status: 'accepted' }] }
result = await ProjectModel.paginate(filter, { page: parseInt(page), limit: parseInt(limit), sort: { createdAt: -1 } })
totalPages = result?.totalPages
totalItems = result?.totalDocs
} else if (type === 'user') {
// جستجو در مدل User با فیلتر کردن برای user_type === "user"
filter = { user_type: 'user', $or: [{ user_name: new RegExp(search, 'i') }, { first_name: new RegExp(search, 'i') }, { last_name: new RegExp(search, 'i') }] }
result = await UserModel.paginate(filter, { page: parseInt(page), limit: parseInt(limit), select: selectedFields })
totalPages = result?.totalPages
totalItems = result?.totalDocs
} else if (type === 'employer') {
// جستجو در مدل User با فیلتر کردن برای user_type === "employer"
filter = { user_type: 'employer', $or: [{ user_name: new RegExp(search, 'i') }, { first_name: new RegExp(search, 'i') }, { last_name: new RegExp(search, 'i') }] }
result = await UserModel.paginate(filter, { page: parseInt(page), limit: parseInt(limit), select: selectedFields })
totalPages = result?.totalPages
totalItems = result?.totalDocs
}
res.status(200).json({
success: true,
result: result?.docs,
type,
totalPages,
totalItems
})
} else {
res.status(422).json({ success: false, message: 'چیزی تایپ کنید' })
}
} catch (error) {
next(error)
}
}
module.exports = {
getSearch
}

View File

@@ -1,51 +0,0 @@
const ProjectModel = require('../../../models/ProjectModel')
const UserModel = require('../../../models/UserModel')
const getSearch = async (req, res, next) => {
try {
const { search, type, page = 1, limit = 10 } = req.query
let result
const selectedFields = '_id user_name first_name last_name user_type is_verified profile_image expertise rate user_level user_score'
let filter
let totalPages
let totalItems
if (search) {
if (!type) {
res.status(422).json({ success: false, message: 'نوع جستجو را مشخص کنید' })
} else if (type === 'project') {
// جستجو در مدل Project با فیلتر کردن برای user_type === "employer"
filter = { $or: [{ title: new RegExp(search, 'i'), status: 'accepted' }, { description: new RegExp(search, 'i'), status: 'accepted' }] }
result = await ProjectModel.paginate(filter, { page: parseInt(page), limit: parseInt(limit), sort: { createdAt: -1 } })
totalPages = result?.totalPages
totalItems = result?.totalDocs
} else if (type === 'user') {
// جستجو در مدل User با فیلتر کردن برای user_type === "user"
filter = { user_type: 'user', $or: [{ user_name: new RegExp(search, 'i') }, { first_name: new RegExp(search, 'i') }, { last_name: new RegExp(search, 'i') }] }
result = await UserModel.paginate(filter, { page: parseInt(page), limit: parseInt(limit), select: selectedFields })
totalPages = result?.totalPages
totalItems = result?.totalDocs
} else if (type === 'employer') {
// جستجو در مدل User با فیلتر کردن برای user_type === "employer"
filter = { user_type: 'employer', $or: [{ user_name: new RegExp(search, 'i') }, { first_name: new RegExp(search, 'i') }, { last_name: new RegExp(search, 'i') }] }
result = await UserModel.paginate(filter, { page: parseInt(page), limit: parseInt(limit), select: selectedFields })
totalPages = result?.totalPages
totalItems = result?.totalDocs
}
res.status(200).json({
success: true,
result: result?.docs,
type,
totalPages,
totalItems
})
} else {
res.status(422).json({ success: false, message: 'چیزی تایپ کنید' })
}
} catch (error) {
next(error)
}
}
module.exports = {
getSearch
}

View File

@@ -1,19 +0,0 @@
// controllers/settingController.js
const SettingModel = require('../../../models/SettingsModel')
// متد برای دریافت تنظیمات بر اساس key
const getSetting = async (req, res, next) => {
try {
const { key } = req.params
const setting = await SettingModel.findOne({ key })
if (!setting) {
return res.status(404).json({ message: 'تنظیمات مورد نظر یافت نشد' })
}
res.status(200).json(setting)
} catch (error) {
next(error)
}
}
module.exports = { getSetting }

View File

@@ -1,147 +0,0 @@
const jwt = require('jsonwebtoken')
const TicketModel = require('../../../models/TicketModel')
const TicketMessageModel = require('../../../models/TicketMessageModel')
const jMoment = require('moment-jalaali')
const path = require('path')
const fs = require('fs-extra')
const getUserTickets = async (req, res) => {
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 { page = 1, limit = 10, search } = req.query // افزودن پارامترهای صفحه‌بندی به درخواست
const filter = { user: userId }
if (search) {
filter.$or = [
{ title: { $regex: search, $options: 'i' } }
]
}
const options = {
page: parseInt(page) || 1,
limit: parseInt(limit) || 10,
sort: { updatedAt: -1 }
}
const tickets = await TicketModel.paginate(filter, options)
const ticketList = tickets?.docs.map(ticket => {
const jDate = jMoment(ticket.createdAt).format('jYYYY-jMM-jDD HH:mm')
return {
...ticket._doc,
createdAt: jDate
}
})
res.json({
tickets: ticketList,
totalPages: tickets.totalPages, // ارسال تعداد کل صفحات
totalItems: tickets.totalDocs // ارسال تعداد کل آیتم‌ها
})
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' })
}
}
const createTicket = async (req, res) => {
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 { title } = req.body
const newTicket = new TicketModel({
title,
user: userId
})
await newTicket.save()
res.status(201).json(newTicket)
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' })
}
}
const addUserMessageToTicket = async (req, res) => {
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 { ticketId } = req.query
const { text } = req.body
const { file } = req.files
let fileUrl = null
if (file) {
// اگر فایل ارسال شده است، آن را ذخیره کنید
const uploadDir = path.join(__dirname, '../../../../storage/tickets')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
const uniqueFileName = `${Date.now()}-${file.name}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(file.path, filePath)
fileUrl = `/tickets/${uniqueFileName}`
}
const newMessage = new TicketMessageModel({
text,
senderType: 'User',
senderId: userId,
ticket: ticketId,
file: fileUrl
})
await newMessage.save()
const ticket = await TicketModel.findById(ticketId)
if (!ticket) {
return res.status(404).json({ error: 'Ticket not found' })
}
ticket.status = 'Customer Response'
await ticket.save()
const newMessageWithJalaliDate = {
...newMessage._doc,
createdAt: jMoment(newMessage.createdAt).format('jYYYY-jMM-jDD HH:mm')
}
res.status(201).json(newMessageWithJalaliDate)
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' })
}
}
const getTicketMessages = async (req, res) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { ticketId } = req.query
const { page = 1, limit = 10 } = req.query
const options = {
page: parseInt(page) || 1,
limit: parseInt(limit) || 10,
sort: { createdAt: -1 }
}
const messages = await TicketMessageModel.paginate({ ticket: ticketId }, options)
const ticket = await TicketModel.findById(ticketId)
ticket.new_message = false
await ticket.save()
const messageList = messages?.docs.map(message => {
const jDate = jMoment(message.createdAt).format('jYYYY-jMM-jDD HH:mm')
return {
...message._doc,
createdAt: jDate
}
})
res.json({
messages: messageList.reverse(),
totalPages: messages.totalPages, // ارسال تعداد کل صفحات
totalItems: messages.totalDocs // ارسال تعداد کل آیتم‌ها
})
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' })
}
}
module.exports = { getUserTickets, createTicket, addUserMessageToTicket, getTicketMessages }

View File

@@ -1,112 +0,0 @@
/* eslint-disable camelcase */
const { ProvinceModel, CityModel } = require('../../../models/StateCity')
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setAddress = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
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') {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی ناقص است'
})
}
// یافتن کاربر با شماره موبایل
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: 'کاربری با این شماره موبایل یافت نشد'
})
}
// یافتن استان و شهر
const province = await ProvinceModel.findOne({ id: province_id })
const city = await CityModel.findOne({ id: city_id })
if (!province || !city) {
return res.status(404).json({
error: true,
message: 'استان یا شهر موردنظر یافت نشد'
})
}
user.province = province
user.city = city
user.address = address
user.lat = lat
user.lng = lng
user.show_location = show_location
await user.save()
return res.json({
message: 'آدرس با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
const updateAddress = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
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) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی ناقص است'
})
}
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
// یافتن کاربر با استفاده از شناسه کاربر
const user = await UserModel.findById(userId)
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شناسه یافت نشد'
})
}
// یافتن استان و شهر با استفاده از شناسه استان و شهر
const province = await ProvinceModel.findOne({ id: province_id })
const city = await CityModel.findOne({ id: city_id })
if (!province || !city) {
return res.status(404).json({
error: true,
message: 'استان یا شهر مورد نظر یافت نشد'
})
}
// به‌روزرسانی اطلاعات آدرس کاربر
user.province = province
user.city = city
user.address = address
user.lat = lat
user.lng = lng
user.show_location = show_location
await user.save()
return res.json({
message: 'آدرس با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
setAddress, updateAddress
}

View File

@@ -1,108 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const { check, validationResult } = require('express-validator')
const jwt = require('jsonwebtoken')
const saveAuthValidationRules = () => {
return [
check('shaba').notEmpty().withMessage('شماره شبا نمی‌تواند خالی باشد')
.matches(/^(?=.{24}$)[0-9]*$/).withMessage('فرمت شماره شبا صحیح نیست'),
check('birthday').notEmpty().withMessage('تاریخ تولد نمی‌تواند خالی باشد'),
check('national_code').notEmpty().withMessage('کد ملی نمی‌تواند خالی باشد')
.isLength({ min: 10, max: 10 }).withMessage('کد ملی باید 10 رقم باشد')
]
}
const saveAuth = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const { shaba, birthday, national_code } = req.body
if (!shaba || !birthday || !national_code) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// جستجوی کاربر با شماره موبایل ارسالی
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: 'کاربری با این شماره موبایل یافت نشد'
})
}
// بررسی تکراری بودن کد ملی
const existingUserWithNationalCode = await UserModel.findOne({ national_code })
if (existingUserWithNationalCode && existingUserWithNationalCode._id.toString() !== user._id.toString()) {
return res.status(409).json({
error: true,
message: 'کد ملی تکراری است'
})
}
user.shaba = shaba
user.birthday = birthday
user.national_code = national_code
await user.save()
return res.json({
message: 'اطلاعات با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
const updateShaba = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const { shaba } = req.body
if (!shaba) {
return res.status(422).json({
error: true,
message: 'شبا ارسال نشده است'
})
}
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
// جستجوی کاربر با استفاده از شناسه کاربر
const user = await UserModel.findById(userId)
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شناسه یافت نشد'
})
}
// فقط شبا را به‌روزرسانی کنید
user.shaba = shaba
await user.save()
return res.json({
message: 'شبا با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
saveAuth, saveAuthValidationRules, updateShaba
}

View File

@@ -1,80 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setColors = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { eye_color, hair_color } = req.body
if (!eye_color || !hair_color) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// جستجوی کاربر با شماره موبایل ارسالی
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.eye_color = eye_color
user.hair_color = hair_color
await user.save()
return res.json({
message: 'رنگ ها با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
const updateColors = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { eye_color, hair_color } = req.body
// اعتبارسنجی داده‌ها
if (!eye_color || !hair_color) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی ناقص است'
})
}
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
// یافتن کاربر با استفاده از شناسه کاربر
const user = await UserModel.findById(userId)
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شناسه یافت نشد'
})
}
// به‌روزرسانی اطلاعات رنگ‌ها
user.eye_color = eye_color
user.hair_color = hair_color
await user.save()
return res.json({
message: 'رنگ‌ها با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
setColors, updateColors
}

View File

@@ -1,74 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setConversation = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { bio, conversation_projects } = req.body
if (!bio || conversation_projects === undefined) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// جستجوی کاربر با شماره موبایل ارسالی
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.bio = bio
user.conversation_projects = conversation_projects
await user.save()
return res.json({
message: 'روابط عمومی با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
const updateConversation = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { bio, conversation_projects } = req.body
if (!bio || conversation_projects === undefined) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
// جستجوی کاربر با استفاده از شناسه کاربر
const user = await UserModel.findById(userId)
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شناسه یافت نشد'
})
}
user.bio = bio
user.conversation_projects = conversation_projects
await user.save()
return res.json({
message: 'روابط عمومی با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
setConversation, updateConversation
}

View File

@@ -1,93 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setCooperationType = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { cooperation_type, cooperation_abroad } = req.body
if (!cooperation_type || cooperation_abroad === undefined) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
if (!['all', 'verified'].includes(cooperation_type)) {
return res.status(422).json({
error: true,
message: 'نوع همکاری نیست'
})
}
// جستجوی کاربر با شماره موبایل ارسالی
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.cooperation_type = cooperation_type
user.cooperation_abroad = cooperation_abroad
await user.save()
return res.json({
message: 'نوع همکاری با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
const updateCooperationType = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { cooperation_type, cooperation_abroad } = req.body
if (!cooperation_type || cooperation_abroad === undefined) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
if (!['all', 'verified'].includes(cooperation_type)) {
return res.status(422).json({
error: true,
message: 'نوع همکاری نامعتبر است'
})
}
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
// جستجوی کاربر با استفاده از شناسه کاربر
const user = await UserModel.findById(userId)
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شناسه یافت نشد'
})
}
user.cooperation_type = cooperation_type
user.cooperation_abroad = cooperation_abroad
await user.save()
return res.json({
message: 'نوع همکاری با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
setCooperationType,
updateCooperationType
}

View File

@@ -1,81 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setExpertise = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { expertise, sub_expertise } = req.body
if (!expertise || !sub_expertise) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// جستجوی کاربر با شماره موبایل ارسالی
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: 'کاربری با این شماره موبایل یافت نشد'
})
}
// آپدیت نام کاربری به صورت lowercase
user.expertise = expertise
user.sub_expertise = sub_expertise
await user.save()
return res.json({
message: 'تخصص با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
const updateExpertise = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { expertise, sub_expertise } = req.body
// اعتبارسنجی داده‌ها
if (!expertise || !sub_expertise) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی ناقص است'
})
}
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
// یافتن کاربر با استفاده از شناسه کاربر
const user = await UserModel.findById(userId)
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شناسه یافت نشد'
})
}
// به‌روزرسانی اطلاعات تخصص و زیرتخصص
user.expertise = expertise
user.sub_expertise = sub_expertise
await user.save()
return res.json({
message: 'تخصص با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
setExpertise, updateExpertise
}

View File

@@ -1,46 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setGender = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { gender } = req.body
if (!gender) {
return res.status(422).json({
error: true,
message: 'اطلاعات ورودی اشتباه است'
})
}
if (!['male', 'female'].includes(gender)) {
return res.status(422).json({
error: true,
message: 'جنسیت معتبر نیست'
})
}
// جستجوی کاربر با شماره موبایل ارسالی
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.gender = gender.toLowerCase()
await user.save()
return res.json({
message: 'جنسیت با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
setGender
}

View File

@@ -1,107 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const fs = require('fs-extra')
const path = require('path')
const setServices = 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: 'شما دسترسی به این بخش ندارید'
})
}
const { services } = req.body
if (!services) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// مدیریت تصاویر برای خدمات
const servicesWithImages = []
const parsedServices = JSON.parse(services) // پارس کردن رشته JSON به آرایه جاوا اسکریپت
if (parsedServices && parsedServices.length > 0) {
for (const service of parsedServices) {
const serviceImages = []
const serviceId = service.id
if (req.files && req.files.serviceImages && req.files.serviceImages[serviceId]) {
const imageFiles = Array.isArray(req.files.serviceImages[serviceId]) ? req.files.serviceImages[serviceId] : [req.files.serviceImages[serviceId]]
const uploadDir = path.join(__dirname, '../../../../storage/services')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
for (const imageFile of imageFiles) {
const uniqueFileName = `${user.user_name}-${Date.now()}${Math.random().toString().slice(2, 11)}${path.extname(imageFile.name)}`
const filePath = path.join(uploadDir, uniqueFileName)
// await fs.promises.rename(imageFile.path, filePath)
await fs.move(imageFile.path, filePath)
const imageUrl = `/services/${uniqueFileName}`
serviceImages.push(imageUrl)
// به‌روزرسانی مسیر تصویر در آبجکت service
service.image = imageUrl
}
} else {
console.log('sss')
}
servicesWithImages.push({ ...service, images: serviceImages })
}
}
user.services = servicesWithImages
await user.save()
return res.json({
message: 'خدمات با موفقیت ثبت شد'
})
} catch (error) {
next(error)
}
}
const updateServices = async (req, res, next) => {
try {
const { height, weight, size } = req.body
if (!height || !weight || !size) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
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(404).json({
error: true,
message: 'کاربر یافت نشد'
})
}
user.height = height
user.weight = weight
user.size = size
await user.save()
return res.json({
message: 'سایز با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
setServices, updateServices
}

View File

@@ -1,55 +0,0 @@
/* eslint-disable camelcase */
const fs = require('fs-extra')
const path = require('path')
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setNationalCardImage = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { national_card_image } = req.files
// جستجوی کاربر با شماره موبایل ارسالی
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: 'کاربری با این شماره موبایل یافت نشد'
})
}
// بررسی آیا فایل آپلود شده است
if (!national_card_image) {
return res.status(400).json({
error: true,
message: 'لطفاً عکس کارت ملی را انتخاب کنید'
})
}
// ذخیره فایل عکس پروفایل
const uploadDir = path.join(__dirname, '../../../../storage/carts')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
const uniqueFileName = `${user.user_name}-${Date.now()}${path.extname(national_card_image.name)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(national_card_image.path, filePath)
// ذخیره مسیر فایل در دیتابیس
user.national_card_image = `/carts/${uniqueFileName}`
user.is_verified = 'pending'
await user.save()
return res.json({
message: 'عکس کارت ملی با موفقیت ذخیره شد',
national_card_image: user.national_card_image
})
} catch (error) {
next(error)
}
}
module.exports = {
setNationalCardImage
}

View File

@@ -1,105 +0,0 @@
/* eslint-disable camelcase */
const fs = require('fs-extra')
const path = require('path')
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setProfileImage = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { mobile } = req.body
const { profile_image } = req.files
if (!mobile) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
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: 'کاربری با این شماره موبایل یافت نشد'
})
}
// بررسی آیا فایل آپلود شده است
if (!profile_image) {
return res.status(400).json({
error: true,
message: 'لطفاً عکس پروفایل را انتخاب کنید'
})
}
// ذخیره فایل عکس پروفایل
const uploadDir = path.join(__dirname, '../../../../storage/profiles')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
const uniqueFileName = `${user.user_name}-${Date.now()}${path.extname(profile_image.name)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(profile_image.path, filePath)
// ذخیره مسیر فایل در دیتابیس
user.profile_image = `/profiles/${uniqueFileName}`
await user.save()
return res.json({
message: 'عکس پروفایل با موفقیت ذخیره شد',
profile_image: user.profile_image
})
} catch (error) {
next(error)
}
}
const updateProfileImage = 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 { profile_image } = req.files
if (!profile_image) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const user = await UserModel.findById(userId)
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
const uploadDir = path.join(__dirname, '../../../../storage/profiles')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
const uniqueFileName = `${user.user_name}-${Date.now()}${path.extname(profile_image.name)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(profile_image.path, filePath)
user.profile_image = `/profiles/${uniqueFileName}`
await user.save()
return res.json({
message: 'عکس پروفایل با موفقیت به‌روزرسانی شد',
profile_image: user.profile_image
})
} catch (error) {
next(error)
}
}
module.exports = {
setProfileImage, updateProfileImage
}

View File

@@ -1,78 +0,0 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const setSizes = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { height, weight, size } = req.body
if (!height || !weight || !size) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// جستجوی کاربر با شماره موبایل ارسالی
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.height = height
user.weight = weight
user.size = size
await user.save()
return res.json({
message: 'سایز با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
const updateSizes = async (req, res, next) => {
try {
const { height, weight, size } = req.body
if (!height || !weight || !size) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
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(404).json({
error: true,
message: 'کاربر یافت نشد'
})
}
user.height = height
user.weight = weight
user.size = size
await user.save()
return res.json({
message: 'سایز با موفقیت به‌روزرسانی شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
setSizes, updateSizes
}

View File

@@ -1,16 +0,0 @@
const VersionModel = require('../../../models/VersionModel')
// Endpoint برای دریافت نسخه فعلی
const getVersion = async (req, res, next) => {
try {
const currentVersion = await VersionModel.findOne().sort({ _id: -1 }).exec()
if (!currentVersion) {
return res.status(200).json({ message: 'نسخه‌ای پیدا نشد' })
}
res.json(currentVersion)
} catch (error) {
res.status(500).json({ message: 'خطای سرور', error })
}
}
module.exports = { getVersion }

View File

@@ -1,232 +0,0 @@
/* eslint-disable camelcase */
const ProjectModel = require('../../../models/ProjectModel')
const RequestModel = require('../../../models/RequestModel')
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
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 getProjects = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
// eslint-disable-next-line no-unused-vars
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
const user = await UserModel.findById(decodedToken.id)
const { status_filter, page = 1, limit = 10 } = req.query // افزودن پارامترهای صفحه‌بندی به درخواست
// ساخت فیلتر برای استفاده در جستجوی MongoDB
let filter = {} // افزودن شرط برای payment_status
if (user.user_type === 'user') {
if (status_filter) {
if (status_filter === 'دریافتی') {
filter = { created_for_user: userId, status: 'accepted' }
} else if (status_filter === 'ارسال شده') {
filter = { requested_users: userId, status: 'accepted' }
} else if (status_filter === 'در دست اقدام') {
filter = { selected_user: userId, status: 'ongoing' }
} else if (status_filter === 'اتمام پروژه') {
filter = { selected_user: userId, status: 'done' }
} else if (status_filter === 'کنسل شده') {
filter = { selected_user: userId, status: 'cancled' }
} else {
filter = {
$or: [
{ selected_user: userId },
{ requested_users: userId },
{ created_for_user: userId }
]
}
}
} else {
filter = {
$or: [
{ selected_user: userId },
{ requested_users: userId },
{ created_for_user: userId }
]
}
}
} else {
if (status_filter) {
if (status_filter === 'منتشر شده') {
filter = { creator_id: userId, status: 'accepted' }
} else if (status_filter === 'در دست اقدام') {
filter = { creator_id: userId, status: 'ongoing' }
} else if (status_filter === 'اتمام پروژه') {
filter = { creator_id: userId, status: 'done' }
} else if (status_filter === 'کنسل شده') {
filter = { creator_id: userId, status: 'cancled' }
} else if (status_filter === 'در دست بررسی') {
filter = { creator_id: userId, status: { $in: ['paid', 'rejected'] } }
} else if (status_filter === 'پرداخت نشده') {
filter = { creator_id: userId, status: 'pre_payment' }
} else {
filter = { creator_id: userId }
}
} else {
filter = { creator_id: userId }
}
}
// paid
// دریافت لیست پروژه‌ها با استفاده از فیلتر
// استفاده از مدل پیجینیت شده برای دریافت نتایج صفحه‌بندی شده
const options = {
page: parseInt(page), // تبدیل صفحه به عدد صحیح
limit: parseInt(limit), // تبدیل محدودیت به عدد صحیح
sort: { createdAt: -1 }
}
const projects = await ProjectModel.paginate(filter, options)
const projectsWithUserInfo = await Promise.all(projects?.docs.map(async project => {
const creator = await UserModel.findById(project.creator_id).select('first_name last_name user_name user_type is_verified profile_image gender province city rate')
const projectWithUserInfo = {
...project._doc,
creator,
status_filter
}
const currentTime = new Date()
const projectCreatedAt = new Date(project.createdAt)
const timeDiff = project.offer_time * 24 * 60 * 60 * 1000 - (currentTime - projectCreatedAt)
const remainingTime = calculateRemainingTime(timeDiff)
projectWithUserInfo.remainingTime = remainingTime
return projectWithUserInfo
}))
res.status(200).json({
projects: projectsWithUserInfo,
totalPages: projects.totalPages, // ارسال تعداد کل صفحات
totalItems: projects.totalDocs // ارسال تعداد کل آیتم‌ها
})
} catch (error) {
next(error)
}
}
const getSingleProject = 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 projectId = req.query.projectId
const projectRequests = await RequestModel.find({ project: projectId })
.populate('user', 'price time user_level first_name last_name user_name is_verified user_score rate profile_image')
.sort({ status: 1 })
.lean()
const modifiedRequests = projectRequests.map(request => ({
...request,
time: request?.user?._id.toString() === userId ? request.time : null,
price: request?.user?._id.toString() === userId ? request.price : null
}))
const project = await ProjectModel.findById(projectId)
if (!project) {
return res.status(404).json({ message: 'پروژه مورد نظر یافت نشد' })
}
// محاسبه مبلغ باقیمانده و مجموع مبلغ پرداخت شده
// تابع محاسبه مبلغ پرداخت شده
const getTotalPaidAmount = (installments) => {
let totalPaidAmount = 0
installments.forEach(installment => {
totalPaidAmount += installment.amount
})
return totalPaidAmount
}
// تابع محاسبه مبلغ باقیمانده
const getRemainingAmount = (project) => {
const totalPaidAmount = getTotalPaidAmount(project.installments)
const remainingAmount = project.final_price - totalPaidAmount
return remainingAmount
}
const totalPaidAmount = getTotalPaidAmount(project.installments)
const remainingAmount = getRemainingAmount(project)
// اطلاعات کاربر سازنده را نیز دریافت کنید و به آبجکت پروژه اضافه کنید
const creator = await UserModel.findById(project.creator_id).select('first_name last_name user_name user_type is_verified profile_image gender province city rate')
const projectWithUserInfo = {
...project._doc,
creator // اضافه کردن اطلاعات کاربر به آبجکت پروژه
}
const selectedUser = await UserModel.findById(project.selected_user).select('_id user_name first_name last_name user_type is_verified profile_image expertise sub_expertise province city rate user_level user_score')
projectWithUserInfo.selected_user = selectedUser
// محاسبه مدت زمان باقی‌مانده برای پروژه
const currentTime = new Date()
const projectCreatedAt = new Date(project.createdAt)
const timeDiff = project.offer_time * 24 * 60 * 60 * 1000 - (currentTime - projectCreatedAt)
const remainingTime = calculateRemainingTime(timeDiff)
projectWithUserInfo.remainingTime = remainingTime
let projectDetails = projectWithUserInfo
if ((project.selected_user && project.selected_user.toString()) === userId || project.creator_id.toString() === userId) {
projectDetails = {
...projectWithUserInfo,
total_paid_amount: totalPaidAmount,
remaining_amount: remainingAmount
}
} else {
projectDetails =
projectWithUserInfo
}
// }
res.status(200).json({ project: projectDetails, projectRequests: modifiedRequests })
} catch (error) {
next(error)
}
}
const getProjectRequests = 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 projectId = req.params.projectId
const project = await ProjectModel.findOne({ _id: projectId, creator_id: userId })
if (!project) {
return res.status(403).send({ message: 'Access denied: You are not the creator of this project.' })
}
// تابع محاسبه مبلغ پرداخت شده
const getTotalPaidAmount = (installments) => {
let totalPaidAmount = 0
installments.forEach(installment => {
totalPaidAmount += installment.amount
})
return totalPaidAmount
}
// تابع محاسبه مبلغ باقیمانده
const getRemainingAmount = (project) => {
const totalPaidAmount = getTotalPaidAmount(project.installments)
const remainingAmount = project.final_price - totalPaidAmount
return remainingAmount
}
// محاسبه مبلغ باقیمانده
const remainingAmount = getRemainingAmount(project)
// یافتن جزییات پروژه
const creator = await UserModel.findById(project.creator_id).select('first_name last_name user_name user_type is_verified profile_image gender province city rate')
const selectedUser = await UserModel.findById(project.selected_user).select('_id user_name first_name last_name user_type is_verified profile_image expertise sub_expertise province city rate user_level user_score')
const totalPaidAmount = getTotalPaidAmount(project.installments)
const projectRequests = await RequestModel.find({ project: projectId }).populate('user', 'price time user_level first_name last_name user_name is_verified user_score rate profile_image').sort({ status: 1 })
const projectinfo = await ProjectModel.findById(projectId) // جزییات درخواست‌های کاربران
projectinfo.selected_user = selectedUser
const projectDetails = {
...projectinfo._doc,
creator,
total_paid_amount: totalPaidAmount,
remaining_amount: remainingAmount
}
res.status(200).json({ projectRequests, projectDetails })
} catch (error) {
next(error)
}
}
module.exports = {
getProjects, getSingleProject, getProjectRequests
}

View File

@@ -1,50 +0,0 @@
const AcademyCategoryModel = require('../../models/AcademyCategoryModel');
// دریافت همه دسته‌بندی‌های اکادمی
exports.getAll = async (req, res) => {
try {
const categories = await AcademyCategoryModel.find({}).sort({ createdAt: -1 });
res.json({ success: true, data: categories });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// ایجاد دسته‌بندی جدید
exports.create = async (req, res) => {
try {
const { title } = req.body;
if (!title) return res.status(400).json({ success: false, message: "عنوان الزامی است" });
const category = await AcademyCategoryModel.create({ title });
res.status(201).json({ success: true, message: "دسته‌بندی ایجاد شد", data: category });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// ویرایش دسته‌بندی
exports.update = async (req, res) => {
try {
const { id } = req.params;
const { title, status } = req.body;
const category = await AcademyCategoryModel.findByIdAndUpdate(id, { title, status }, { new: true });
if (!category) return res.status(404).json({ success: false, message: "دسته‌بندی یافت نشد" });
res.json({ success: true, message: "دسته‌بندی ویرایش شد", data: category });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
// حذف دسته‌بندی
exports.remove = async (req, res) => {
try {
const { id } = req.params;
await AcademyCategoryModel.findByIdAndDelete(id);
res.json({ success: true, message: "دسته‌بندی حذف شد" });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};

View File

@@ -1,350 +0,0 @@
/* eslint-disable eqeqeq */
/* eslint-disable camelcase */
const { ProvinceModel, CityModel } = require('../../../models/StateCity')
const AdvertisingModel = require('../../../models/AdvertisingModel')
// const AdvertisingLikeModel = require('../../../models/AdvertisingLikeModel')
// const AdvertisingComment = require('../../../models/AdvertisingCommentModel')
const AdvertisingProfileModel = require('../../../models/AdvertisingProfile')
const NotificationModel = require('../../../models/NotificationModel')
const AdvertisingCategoryModel = require('../../../models/AdvertisingCategoryModel')
const getAdvertisings = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const {
page = 1, limit = 10, province, city, category, search,
startDate,
endDate,
status
} = req.query
const filter = {
}
if (province) {
const provinceFind = await ProvinceModel.findOne({ id: province }, { _id: 0 })
filter.province = provinceFind
}
if (city) {
const cityFind = await CityModel.findOne({ id: city })
filter.city = cityFind
}
if (category) {
filter.category = category
}
if (search) {
filter.$or = [
{ title: { $regex: search, $options: 'i' } },
{ category: { $regex: search, $options: 'i' } }
]
}
// Date
if (startDate || endDate) {
filter.createdAt = {}
if (startDate) {
filter.createdAt.$gte = new Date(startDate) // تاریخ شروع
}
if (endDate) {
filter.createdAt.$lte = new Date(endDate) // تاریخ پایان
}
}
if (status) {
filter.status = status
}
const options = {
page: parseInt(page),
limit: parseInt(limit),
sort: { createdAt: -1 },
populate: [
{ path: 'creator_id', select: 'user_name first_name last_name' }
]
}
const advertisings = await AdvertisingModel.paginate(filter, options)
const newAds = advertisings.docs.map(ad => {
return {
// فیلدهای مشخص‌شده از ad._doc
_id: ad._doc._id,
creator_id: ad._doc.creator_id,
category: ad._doc.category,
title: ad._doc.title,
province: ad._doc.province,
city: ad._doc.city,
status: ad._doc.status,
neighbourhood: ad._doc.neighbourhood
}
})
res.status(200).json({
advertisings: newAds,
totalPages: advertisings.totalPages,
totalItems: advertisings.totalDocs
})
} catch (error) {
next(error)
}
}
const getSingleAdvertising = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const advertisingId = req.query.advertisingId
const advertising = await AdvertisingModel.findById(advertisingId)
.populate('creator_id', '_id user_name first_name last_name')
if (!advertising) {
return res.status(404).json({ message: 'ویترین مورد نظر یافت نشد' })
}
const creator = await AdvertisingProfileModel.findOne({ user: advertising.creator_id._id })
// اضافه کردن تعداد لایک‌ها، تعداد کامنت‌ها و وضعیت لایک به تبلیغ
const advertisingData = {
...advertising._doc
}
res.status(200).json({
advertising: advertisingData,
creator
})
} catch (error) {
next(error)
}
}
const acceptAdvertising = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { advertisingId } = req.body
// پیدا کردن تبلیغ
const advertising = await AdvertisingModel.findById(advertisingId)
if (!advertising) {
return res.status(404).json({ message: 'تبلیغ مورد نظر یافت نشد' })
}
// تغییر وضعیت به accepted و ذخیره زمان قبول شدن
advertising.status = 'accepted'
advertising.acceptedAt = new Date()
await advertising.save()
const notification = new NotificationModel({
user_id: advertising?.creator_id,
project_post_id: advertising?._id,
type: 'vitrine',
title: 'تایید ویترین',
description: `ویترین شما با عنوان: ${advertising?.title} تایید و منتشر شد `
})
await notification.save()
res.status(200).json({ message: 'تبلیغ اکسپت شد و پس از 15 روز منقضی خواهد شد' })
} catch (error) {
next(error)
}
}
const rejectAdvertising = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { advertisingId, rejectReason } = req.body
// پیدا کردن تبلیغ
const advertising = await AdvertisingModel.findById(advertisingId)
if (!advertising) {
return res.status(404).json({ message: 'تبلیغ مورد نظر یافت نشد' })
}
// تغییر وضعیت به rejected و ذخیره دلیل رد شدن
advertising.status = 'rejected'
advertising.reject_reason = rejectReason
await advertising.save()
const notification = new NotificationModel({
user_id: advertising?.creator_id,
project_post_id: advertising?._id,
type: 'vitrine',
title: 'رد ویترین',
description: `ویترین شما با عنوان: ${advertising?.title} رد شد، برای دیدن علت رد، لمس کنید `
})
await notification.save()
res.status(200).json({ message: 'تبلیغ رد شد', advertising })
} catch (error) {
next(error)
}
}
const deleteAdvertisingCategory = async (req, res, next) => {
try {
const { id } = req.params
// چک کنه که ID معتبره
if (!id.match(/^[0-9a-fA-F]{24}$/)) {
return res.status(400).json({
success: false,
message: 'شناسه معتبر نمی‌باشد'
})
}
// پیدا کنه دسته‌بندی رو
const category = await AdvertisingCategoryModel.findById(id)
if (!category) {
return res.status(404).json({
success: false,
message: 'دسته‌بندی مورد نظر یافت نشد'
})
}
// حذف کنه
await category.deleteOne()
return res.status(200).json({
success: true,
message: 'دسته‌بندی تبلیغات با موفقیت حذف شد',
data: { id }
})
} catch (error) {
next(error)
}
}
const createAdvertisingCategory = async (req, res, next) => {
try {
const { title, status } = req.body
// اعتبارسنجی ورودی‌ها
if (!title || title.trim().length < 1) {
return res.status(400).json({
success: false,
message: 'عنوان دسته‌بندی الزامی است'
})
}
if (title.length > 255) {
return res.status(400).json({
success: false,
message: 'عنوان دسته‌بندی نباید بیشتر از ۲۵۵ کاراکتر باشد'
})
}
// بررسی اینکه دسته‌بندی تکراری نباشد
const exists = await AdvertisingCategoryModel.findOne({ title: title.trim() })
if (exists) {
return res.status(409).json({
success: false,
message: 'این دسته‌بندی از قبل وجود دارد'
})
}
// ایجاد دسته‌بندی جدید
const newCategory = await AdvertisingCategoryModel.create({
title: title.trim(),
status: status ?? true
})
return res.status(201).json({
success: true,
message: 'دسته‌بندی تبلیغات با موفقیت ایجاد شد',
data: newCategory
})
} catch (error) {
next(error) // میره به errorHandler مرکزی
}
}
const getAdvertisingCategories = async (req, res, next) => {
try {
const page = Math.max(parseInt(req.query.page || '1', 10), 1);
const limit = Math.max(Math.min(parseInt(req.query.limit || '20', 10), 100), 1);
const { status, q, sort } = req.query;
const query = {};
if (typeof status !== 'undefined') {
// accept 'true'/'false' or boolean
if (status === 'true' || status === 'false') query.status = status === 'true';
else if (status === '1' || status === '0') query.status = status === '1';
}
if (q) {
query.title = { $regex: q.trim(), $options: 'i' };
}
const options = {
page,
limit,
sort: sort || '-createdAt',
lean: true,
};
// اگر mongoose-paginate-v2 نصب و فعال است:
if (typeof AdvertisingCategoryModel.paginate === 'function') {
const result = await AdvertisingCategoryModel.paginate(query, options);
return res.status(200).json({
success: true,
totalDocs: result.totalDocs,
totalPages: result.totalPages,
page: result.page,
limit: result.limit,
docs: result.docs,
});
}
// fallback بدون paginate
const docs = await AdvertisingCategoryModel.find(query)
.sort(options.sort)
.skip((page - 1) * limit)
.limit(limit)
.lean();
const totalDocs = await AdvertisingCategoryModel.countDocuments(query);
const totalPages = Math.ceil(totalDocs / limit);
return res.status(200).json({
success: true,
totalDocs,
totalPages,
page,
limit,
docs,
});
} catch (error) {
next(error);
}
};
/**
* GET /advertising/advertising-categories/:id
*/
const getAdvertisingCategoryById = async (req, res, next) => {
try {
const { id } = req.params;
if (!id || !id.match(/^[0-9a-fA-F]{24}$/)) {
return res.status(400).json({ success: false, message: 'شناسه معتبر نیست' });
}
const category = await AdvertisingCategoryModel.findById(id).lean();
if (!category) {
return res.status(404).json({ success: false, message: 'دسته‌بندی یافت نشد' });
}
return res.status(200).json({ success: true, data: category });
} catch (error) {
next(error);
}
};
module.exports = {
getSingleAdvertising,
getAdvertisings,
deleteAdvertisingCategory,
createAdvertisingCategory,
getAdvertisingCategories,
getAdvertisingCategoryById,
acceptAdvertising,
rejectAdvertising
}

View File

@@ -1,195 +0,0 @@
/* eslint-disable camelcase */
const AdvertisingComment = require('../../../models/AdvertisingCommentModel')
const jMoment = require('moment-jalaali')
const mongoose = require('mongoose')
const AdvertisingModel = require('../../../models/AdvertisingModel')
const NotificationModel = require('../../../models/NotificationModel')
// دریافت کامنت‌ها
const getAdvertisingComments = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const {
page = 1, limit = 10,
startDate,
endDate,
status
} = req.query
const filter = {}
// Date
if (startDate || endDate) {
filter.createdAt = {}
if (startDate) {
filter.createdAt.$gte = new Date(startDate) // تاریخ شروع
}
if (endDate) {
filter.createdAt.$lte = new Date(endDate) // تاریخ پایان
}
}
if (status) {
filter.status = status
}
const options = {
page: parseInt(page),
limit: parseInt(limit),
sort: { createdAt: -1 },
populate: [
{ path: 'userId', select: 'user_name first_name last_name profile_image' },
{ path: 'advertisingId', select: 'title', populate: { path: 'creator_id', select: 'user_name first_name last_name' } }
// اضافه کردن اطلاعات سازنده
]
}
const comments = await AdvertisingComment.paginate(filter, options)
const commentList = comments.docs.map(comment => ({
_id: comment._id,
createdAt: jMoment(comment.createdAt).format('jYYYY-jMM-jDD HH:mm'),
status: comment.status,
text: comment.text,
advertising: comment.advertisingId ? comment.advertisingId.title : null,
user: comment.userId ? {
_id: comment.userId._id,
user_name: comment.userId.user_name,
first_name: comment.userId.first_name,
last_name: comment.userId.last_name,
profile_image: comment.userId.profile_image
} : null,
creator: (comment.advertisingId && comment.advertisingId.creator_id) ? {
_id: comment.advertisingId.creator_id._id,
user_name: comment.advertisingId.creator_id.user_name,
first_name: comment.advertisingId.creator_id.first_name,
last_name: comment.advertisingId.creator_id.last_name
} : null
}));
res.status(200).json({
comments: commentList,
totalPages: comments.totalPages,
totalItems: comments.totalDocs
})
} catch (error) {
next(error)
}
}
// دریافت جزئیات یک کامنت
const getAdvertisingCommentDetail = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const comment_id = req.query.comment_id
if (!mongoose.Types.ObjectId.isValid(comment_id)) {
return res.status(400).json({ message: 'شناسه کامنت معتبر نیست' })
}
const comment = await AdvertisingComment.findById(comment_id)
.populate('userId', 'user_name first_name last_name profile_image')
.populate({
path: 'advertisingId',
select: 'title creator_id',
populate: {
path: 'creator_id',
select: 'user_name first_name last_name'
}
})
if (!comment) {
return res.status(404).json({ message: 'کامنت مورد نظر یافت نشد' })
}
const response = {
_id: comment._id,
text: comment.text,
createdAt: jMoment(comment.createdAt).format('jYYYY-jMM-jDD HH:mm'),
status: comment.status,
advertising: comment.advertisingId ? comment.advertisingId.title : null,
creator: {
_id: comment?.advertisingId?.creator_id._id,
user_name: comment?.advertisingId?.creator_id.user_name,
first_name: comment?.advertisingId?.creator_id.first_name,
last_name: comment?.advertisingId?.creator_id.last_name
},
user: {
_id: comment.userId._id,
user_name: comment.userId.user_name,
first_name: comment.userId.first_name,
last_name: comment.userId.last_name,
profile_image: comment.userId.profile_image
}
}
res.status(200).json({ comment: response })
} catch (error) {
next(error)
}
}
// تایید یک کامنت
const acceptAdvertisingComment = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { id, status, text } = req.body
if (!id || !status) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const comment = await AdvertisingComment.findById(id)
// افزایش تعداد کامنت‌های تبلیغ
await AdvertisingModel.findByIdAndUpdate(comment?.advertisingId, { $inc: { commentsCount: 1 } })
if (!comment) {
return res.status(404).json({
error: true,
message: 'کامنت مورد نظر یافت نشد'
})
}
if (text) {
comment.text = text
}
comment.status = status
await comment.save()
if (status === 'accepted') {
const notification = new NotificationModel({
user_id: comment?.userId,
project_post_id: comment?.advertisingId,
type: 'vitrine-comment',
title: 'انتشار نظر',
description: 'نظر شما منتشر شد.'
})
await notification.save()
} else {
const notification = new NotificationModel({
user_id: comment?.userId,
project_post_id: comment?.advertisingId,
type: 'vitrine-comment',
title: 'رد نظر',
description: 'نظر شما رد شد.'
})
await notification.save()
}
res.status(201).json({ message: 'کامنت با موفقیت تایید شد' })
} catch (error) {
next(error)
}
}
module.exports = {
getAdvertisingComments,
getAdvertisingCommentDetail,
acceptAdvertisingComment
}

View File

@@ -1,172 +0,0 @@
/* eslint-disable camelcase */
const CommentModel = require('../../../models/CommentModel')
const jMoment = require('moment-jalaali')
const { default: mongoose } = require('mongoose')
// دریافت لیست کامنت‌ها با پیجینیشن
const getComments = async (req, res, next) => {
try {
console.log("gg6");
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const {
page = 1, limit = 10,
startDate,
endDate,
status
} = req.query
const filter = {} // فیلتر بر اساس وضعیت کامنت
// Date
if (startDate || endDate) {
filter.createdAt = {}
if (startDate) {
filter.createdAt.$gte = new Date(startDate) // تاریخ شروع
}
if (endDate) {
filter.createdAt.$lte = new Date(endDate) // تاریخ پایان
}
}
console.log("gg8");
if (status) {
filter.status = status
}
const options = {
page: parseInt(page), // تبدیل صفحه به عدد صحیح
limit: parseInt(limit), // تبدیل محدودیت به عدد صحیح
sort: { createdAt: -1 },
populate: [
{ path: 'creator', select: 'user_name first_name last_name profile_image ' },
{ path: 'user', select: 'user_name first_name last_name' },
{ path: 'project', select: 'title' }
]
}
const comments = await CommentModel.paginate(filter, options)
const commentList = comments.docs.map(comment => ({
_id: comment._id,
createdAt: jMoment(comment.createdAt).format('jYYYY-jMM-jDD HH:mm'),
status: comment.status,
comment_for: comment.comment_for,
project: comment.project ? comment.project.title : null,
creator: comment.creator ? {
_id: comment.creator._id,
user_name: comment.creator.user_name,
first_name: comment.creator.first_name,
last_name: comment.creator.last_name,
profile_image: comment.creator.profile_image
} : null,
user: comment.user ? {
_id: comment.user._id,
user_name: comment.user.user_name,
first_name: comment.user.first_name,
last_name: comment.user.last_name
} : null
}));
console.log("gg9");
res.status(200).json({
comments: commentList,
totalPages: comments.totalPages,
totalItems: comments.totalDocs
})
} catch (error) {
console.log("gg");
next(error)
}
}
// دریافت جزئیات یک کامنت
const getCommentDetail = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const comment_id = req.query.comment_id
if (!mongoose.Types.ObjectId.isValid(comment_id)) {
return res.status(400).json({ message: 'شناسه کامنت معتبر نیست' })
}
const comment = await CommentModel.findById(comment_id)
.populate('creator', 'user_name first_name last_name profile_image ')
.populate('user', 'user_name first_name last_name')
.populate('project', 'title')
if (!comment) {
return res.status(404).json({ message: 'کامنت مورد نظر یافت نشد' })
}
const response = {
_id: comment._id,
comment: comment.comment,
rating: comment.rating,
createdAt: jMoment(comment.createdAt).format('jYYYY-jMM-jDD HH:mm'),
status: comment.status,
comment_for: comment.comment_for,
project: comment.project ? comment.project.title : null,
creator: {
_id: comment.creator._id,
user_name: comment.creator.user_name,
first_name: comment.creator.first_name,
last_name: comment.creator.last_name,
profile_image: comment.creator.profile_image
},
user: {
_id: comment.user._id,
user_name: comment.user.user_name,
first_name: comment.user.first_name,
last_name: comment.user.last_name
}
}
res.status(200).json({ comment: response })
} catch (error) {
next(error)
}
}
// تایید یک کامنت
const acceptComment = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { id, status, text } = req.body
if (!id || !status) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const comment = await CommentModel.findById(id)
if (!comment) {
return res.status(404).json({
error: true,
message: 'کامنت مورد نظر یافت نشد'
})
}
if (text) {
comment.comment = text
}
comment.status = status
await comment.save()
res.status(201).json({ message: 'کامنت با موفقیت تایید شد' })
} catch (error) {
next(error)
}
}
module.exports = {
getComments,
getCommentDetail,
acceptComment
}

View File

@@ -1,111 +0,0 @@
/* eslint-disable camelcase */
const ExpertiseModel = require('../../../models/ExpertiseModel')
const createExpertise = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { expertise } = req.body
if (!expertise
) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// بررسی وجود تخصص با این نام در پایگاه داده
const existingExpertise = await ExpertiseModel.findOne({ expertise })
if (existingExpertise) {
return res.status(400).json({ message: 'تخصص با این نام قبلاً ثبت شده است' })
}
// ایجاد یک نمونه جدید از مدل ExpertiseModel
const newExpertise = new ExpertiseModel({
expertise
})
// ذخیره تخصص جدید در پایگاه داده
await newExpertise.save()
res.json({ message: 'تخصص با موفقیت اضافه شد' })
} catch (error) {
next(error)
}
}
const createSubExpertise = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const expertiseId = req.query.id
const { name } = req.body
if (!expertiseId || !name) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// یافتن تخصص موردنظر از طریق آیدی
const expertise = await ExpertiseModel.findById(expertiseId)
if (!expertise) {
return res.status(404).json({ message: 'تخصص موردنظر یافت نشد' })
}
// اضافه کردن زیرتخصص جدید به آرایه sub_expertise تخصص موجود
expertise.sub_expertise.push({ name })
await expertise.save()
res.json({ message: 'زیرتخصص با موفقیت به تخصص اضافه شد' })
} catch (error) {
next(error)
}
}
const getExpertise = async (req, res, next) => {
try {
const expertises = await ExpertiseModel.find().select('expertise sub_expertise')
res.json({ expertises })
} catch (error) {
next(error)
}
}
// ویرایش تخصص
const editExpertise = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { expertise } = req.body
const expertiseId = req.params.id
if (!expertise || !expertiseId) {
return res.status(422).json({ message: 'اطلاعات ارسالی اشتباه است' })
}
const updatedExpertise = await ExpertiseModel.findByIdAndUpdate(
expertiseId,
{ expertise },
{ new: true }
)
if (!updatedExpertise) {
return res.status(404).json({ message: 'تخصص یافت نشد' })
}
res.json({ message: 'تخصص با موفقیت ویرایش شد' })
} catch (error) {
next(error)
}
}
module.exports = {
createExpertise,
createSubExpertise,
getExpertise,
editExpertise
}

View File

@@ -1,108 +0,0 @@
/* eslint-disable camelcase */
const { default: mongoose } = require('mongoose')
const PaymentModel = require('../../../models/PaymentModel')
const jMoment = require('moment-jalaali')
const getFinancial = async (req, res) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const {
page = 1, limit = 10,
startDate,
endDate,
status
} = req.query
const filter = {} // افزودن شرط‌های دیگر برای فیلتر
// Date
if (startDate || endDate) {
filter.createdAt = {}
if (startDate) {
filter.createdAt.$gte = new Date(startDate) // تاریخ شروع
}
if (endDate) {
filter.createdAt.$lte = new Date(endDate) // تاریخ پایان
}
}
if (status) {
filter.status = status
}
const options = {
page: parseInt(page), // تبدیل صفحه به عدد صحیح
limit: parseInt(limit), // تبدیل محدودیت به عدد صحیح
sort: { createdAt: -1 },
populate: [
{
path: 'user_id',
select: '_id user_name first_name mobile last_name '
},
{
path: 'project_id',
select: '_id title project_type'
}
]
}
const financial = await PaymentModel.paginate(
filter, // فیلتر
options // گزینه‌های پیجینیشن
)
const financialList = financial?.docs.map(financia => {
const jDate = jMoment(financia.createdAt).format('jYYYY-jMM-jDD HH:mm')
return {
...financia._doc,
createdAt: jDate
}
})
res.json({
financials: financialList,
totalPages: financial.totalPages, // ارسال تعداد کل صفحات
totalItems: financial.totalDocs // ارسال تعداد کل آیتم‌ها
})
} catch (error) {
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
const getFinancialDetail = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const financial_id = req.query.financial_id
if (!mongoose.Types.ObjectId.isValid(financial_id)) {
return res.status(400).json({ message: 'شناسه تراکنش معتبر نیست' })
}
const financial = await PaymentModel.findById(financial_id).populate([
{
path: 'user_id',
select: '_id user_name first_name mobile last_name shaba'
},
{
path: 'project_id',
select: '_id title project_type',
populate: {
path: 'selected_user',
select: '_id user_name first_name mobile last_name shaba'
}
}
])
if (!financial) {
return res.status(404).json({ message: 'تراکنش مورد نظر یافت نشد' })
}
const newfinancial = {
...financial._doc,
createdAt: jMoment(financial.createdAt).format('jYYYY-jMM-jDD HH:mm')
}
return res.status(200).json({ financial: newfinancial })
} catch (error) {
console.error('Error occurred:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
module.exports = { getFinancial, getFinancialDetail }

View File

@@ -1,94 +0,0 @@
/* eslint-disable camelcase */
const AdminModel = require('../../../models/AdminModel')
const bcrypt = require('bcryptjs')
const TokenService = require('../../../services/TokenService')
const adminLogin = async (req, res, next) => {
try {
// اعتبارسنجی ورودی ها
const { user_name, password } = req.body
if (!user_name || !password) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// یافتن کاربر با نام کاربری
const userLow = user_name.toLowerCase()
const user = await AdminModel.findOne({ user_name: userLow })
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این نام کاربری یافت نشد'
})
}
if (!user.password) {
return res.status(422).json({
error: true,
message: 'رمز عبور برای این کاربر تنظیم نشده است'
})
}
const isPasswordValid = await bcrypt.compare(password, user.password)
if (!isPasswordValid) {
return res.status(422).json({
error: true,
message: 'نام کاربری یا رمز عبور اشتباه است'
})
}
// اگر هم نام کاربری و هم رمز عبور درست بود، ارسال پیام موفقیت آمیز
const token = TokenService.sign({ id: user._id })
return res.json({
message: 'کد تایید صحیح بود',
token,
id: user._id
})
} catch (error) {
next(error)
}
}
const changePassword = async (req, res, next) => {
try {
// اعتبارسنجی ورودی ها
const { user_name, password, new_password } = req.body
if (!user_name || !password || !new_password) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// یافتن کاربر با نام کاربری
const userLow = user_name.toLowerCase()
const user = await AdminModel.findOne({ user_name: userLow })
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این نام کاربری یا رمز عبور یافت نشد'
})
}
const isPasswordValid = await bcrypt.compare(password, user.password)
if (!isPasswordValid) {
return res.status(422).json({
error: true,
message: 'نام کاربری یا رمز عبور اشتباه است'
})
}
// هش کردن پسورد جدید و ذخیره در دیتابیس
const hashedPassword = await bcrypt.hash(new_password, 10)
user.password = hashedPassword
await user.save()
return res.json({
message: 'کلمه عبور با موفقیت ذخیره شد'
})
} catch (error) {
next(error)
}
}
module.exports = {
adminLogin, changePassword
}

View File

@@ -1,176 +0,0 @@
/* eslint-disable camelcase */
const NotificationModel = require('../../../models/NotificationModel')
const PostModel = require('../../../models/PostModel')
const UserModel = require('../../../models/UserModel')
const jMoment = require('moment-jalaali')
const { default: mongoose } = require('mongoose')
const getPosts = async (req, res, next) => {
try {
const authHeader = req.header('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ message: 'Access Denied: No token provided' });
}
const token = authHeader.split(' ')[1];
const {
page = 1,
limit = 10,
startDate,
endDate,
status,
} = req.query;
const filter = {};
if (startDate || endDate) {
filter.createdAt = {};
if (startDate) {
filter.createdAt.$gte = new Date(startDate);
}
if (endDate) {
filter.createdAt.$lte = new Date(endDate);
}
}
if (status) {
filter.status = status;
}
const options = {
page: parseInt(page),
limit: parseInt(limit),
sort: { createdAt: -1 },
populate: {
path: 'user_id',
select: '_id user_name first_name mobile last_name national_code user_type expertise',
},
};
const posts = await PostModel.paginate(filter, options);
const postList = posts.docs.map((post) => ({
_id: post._id,
post_image: post.post_images && post.post_images.length > 0 ? post.post_images[0] : post.post_video || null, // استفاده از اولین تصویر یا ویدئو
status: post.status,
post_images: post.post_images,
post_video: post.post_video,
type: post.type,
files: post.files,
user_id: post.user_id?._id || null,
user_name: post.user_id?.user_name || '-',
user_type: post.user_id?.user_type || '-',
expertise: post.user_id?.expertise || '-',
first_name: post.user_id?.first_name || '-',
last_name: post.user_id?.last_name || '-',
mobile: post.user_id?.mobile || '-',
national_code: post.user_id?.national_code || '-',
createdAt: post.createdAt ? jMoment(post.createdAt).format('jYYYY-jMM-jDD HH:mm') : '-',
}));
res.status(200).json({
posts: postList,
totalPages: posts.totalPages,
totalItems: posts.totalDocs,
});
} catch (error) {
console.error('Error in getPosts:', error);
res.status(500).json({ message: 'خطا در دریافت پست‌ها.' });
}
};
const getPostDetail = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const post_id = req.query.post_id
// eslint-disable-next-line no-unused-vars
if (!mongoose.Types.ObjectId.isValid(post_id)) {
return res.status(400).json({ message: 'شناسه پست معتبر نیست' })
}
const post = await PostModel.findById(post_id).populate('user_id', '_id user_name first_name last_name profile_image user_level')
if (!post) {
return res.status(404).json({ message: 'پست مورد نظر یافت نشد' })
}
const response = {
_id: post._id,
post_image: post.post_images && post.post_images.length > 0 ? post.post_images[0] : post.post_video || null, // استفاده از اولین تصویر یا ویدئو
status: post.status,
caption: post.caption,
comments: post.comments,
post_images: post.post_images,
post_video: post.post_video,
type: post.type,
files: post.files,
user_id: post.user_id?._id || null,
user_name: post.user_id?.user_name || '-',
user_type: post.user_id?.user_type || '-',
expertise: post.user_id?.expertise || '-',
first_name: post.user_id?.first_name || '-',
last_name: post.user_id?.last_name || '-',
mobile: post.user_id?.mobile || '-',
national_code: post.user_id?.national_code || '-',
createdAt: post.createdAt ? jMoment(post.createdAt).format('jYYYY-jMM-jDD HH:mm') : '-',
}
return res.status(200).json({ post: response })
} catch (error) {
next(error)
}
}
const acceptPost = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { id, user_id, status } = req.body
if (!id || !user_id || !status) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const user = await UserModel.findById(user_id)
const post = await PostModel.findById(id)
if (!post) {
return res.status(404).json({
error: true,
message: 'پست مورد نظر یافت نشد'
})
}
post.status = status
await post.save()
if (status === 'accept') {
user.last_post = post
await user.save()
const notification = new NotificationModel({
user_id: user?._id,
project_post_id: post?._id,
type: 'accept-post',
title: 'انتشار پست',
description: 'پست شما مورد تایید قرار گرفت.'
})
await notification.save()
} else {
const notification = new NotificationModel({
user_id: user?._id,
project_post_id: post?._id,
type: 'reject-post',
title: 'رد پست',
description: 'پست شما مورد تایید قرار نگرفت.'
})
await notification.save()
}
res.status(201).json({ message: 'پست با موفقیت تایید شد' })
} catch (error) {
next(error)
}
}
module.exports = {
acceptPost, getPosts, getPostDetail
}

View File

@@ -1,256 +0,0 @@
/* eslint-disable camelcase */
const ProjectModel = require('../../../models/ProjectModel')
const jMoment = require('moment-jalaali')
const RequestModel = require('../../../models/RequestModel')
const NotificationModel = require('../../../models/NotificationModel')
const getProjects = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
// eslint-disable-next-line no-unused-vars
const {
page = 1, limit = 10,
startDate,
endDate,
status
} = req.query
// ساخت فیلتر برای استفاده در جستجوی MongoDB
const filter = {
} // افزودن شرط‌های دیگر برای فیلتر
// Date
if (startDate || endDate) {
filter.createdAt = {}
if (startDate) {
filter.createdAt.$gte = new Date(startDate) // تاریخ شروع
}
if (endDate) {
filter.createdAt.$lte = new Date(endDate) // تاریخ پایان
}
}
if (status) {
filter.status = status
}
// دریافت لیست پروژه‌ها با استفاده از فیلتر و گزینه‌های صفحه‌بندی
const options = {
page: parseInt(page), // تبدیل صفحه به عدد صحیح
limit: parseInt(limit), // تبدیل محدودیت به عدد صحیح
sort: { createdAt: -1 },
populate: [
{ path: 'creator_id', select: '_id user_name first_name last_name' },
{ path: 'selected_user', select: '_id user_name first_name last_name' }
]
}
const projects = await ProjectModel.paginate(filter, options)
const projectsList = projects.docs.map(project => ({
_id: project._id,
title: project.title,
expertise: project.expertise,
sub_expertise: project.sub_expertise,
offer_time: project.offer_time,
offer_price: project.offer_price,
final_price: project.final_price,
final_time: project.final_time,
createdAt: jMoment(project.createdAt).format('jYYYY-jMM-jDD HH:mm'),
status: project.status,
creator: project.creator_id
? {
_id: project.creator_id._id,
user_name: project.creator_id.user_name,
first_name: project.creator_id.first_name,
last_name: project.creator_id.last_name
}
: null,
selected_user: project.selected_user
? {
_id: project.selected_user._id,
user_name: project.selected_user.user_name,
first_name: project.selected_user.first_name,
last_name: project.selected_user.last_name
}
: null
}))
res.status(200).json({
projects: projectsList,
totalPages: projects.totalPages, // ارسال تعداد کل صفحات
totalItems: projects.totalDocs // ارسال تعداد کل آیتم‌ها
})
} catch (error) {
next(error)
}
}
const getProjectDetails = async (req, res, next) => {
try {
const { project_id } = req.query
// بررسی وجود projectId
if (!project_id) {
return res.status(400).send('Project ID is required')
}
// یافتن پروژه و جمع‌آوری اطلاعات مرتبط با سازنده و کاربر انتخاب شده
const project = await ProjectModel.findById(project_id)
.populate('creator_id', '_id user_name first_name last_name mobile')
.populate('selected_user', '_id user_name first_name last_name mobile')
.lean()
// بررسی وجود پروژه
if (!project) {
return res.status(404).send('Project not found')
}
// دریافت لیست درخواست‌های پروژه
const projectRequests = await RequestModel.find({ project: project_id })
.populate('user', '_id first_name last_name user_name rate profile_image')
.sort({ status: 1 })
.lean()
// ساختاردهی پاسخ نهایی
const projectDetails = {
_id: project._id,
title: project.title,
description: project.description,
creator: project.creator_id
? {
_id: project.creator_id._id,
first_name: project.creator_id.first_name,
last_name: project.creator_id.last_name,
user_name: project.creator_id.user_name,
mobile: project.creator_id.mobile
}
: null,
selected_user: project.selected_user
? {
_id: project.selected_user._id,
first_name: project.selected_user.first_name,
last_name: project.creator_id.last_name,
user_name: project.selected_user.user_name,
mobile: project.selected_user.mobile
}
: null,
province: project.province,
city: project.city,
createdAt: jMoment(project.createdAt).format('jYYYY-jMM-jDD HH:mm'),
expertise: project.expertise,
sub_expertise: project.sub_expertise,
offer_time: project.offer_time,
offer_price: project.offer_price,
final_price: project.final_price,
final_time: project.final_time,
status: project.status,
reject_reason: project.reject_reason,
requested_users: projectRequests.map(request => ({
_id: request.user._id,
first_name: request.user.first_name,
last_name: request.user.last_name,
user_name: request.user.user_name,
profile_image: request.user.profile_image,
time: request.time,
price: request.price
}))
}
res.status(200).json({ project: projectDetails })
} catch (error) {
next(error)
}
}
const acceptProject = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { id, user_name } = req.body
if (!id) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const project = await ProjectModel.findById(id)
if (!project) {
return res.status(404).json({
error: true,
message: 'پست مورد نظر یافت نشد'
})
}
project.acceptedAt = new Date()
project.status = 'accepted'
await project.save()
const notification = new NotificationModel({
user_id: project?.creator_id,
project_post_id: project?._id,
type: 'accept-project',
title: 'انتشار درخواست',
description: `درخواست شما با عنوان: ${project?.title} منتشر شد `
})
await notification.save()
if (project?.public_status === 'private' && project?.created_for_user) {
const notification = new NotificationModel({
user_id: project?.created_for_user,
project_post_id: project?._id,
type: 'recive-project',
title: 'دعوت به همکاری',
description: `کاربر ${user_name} برای شما پروژه ایجاد کرده است ، برای نمایش جزئیات لمس کنید`
})
await notification.save()
}
res.status(201).json({ message: 'پروژه با موفقیت تایید شد' })
} catch (error) {
next(error)
}
}
const rejectProject = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { id, rejectReason } = req.body
if (!id) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const project = await ProjectModel.findById(id)
if (!project) {
return res.status(404).json({
error: true,
message: 'پست مورد نظر یافت نشد'
})
}
project.status = 'rejected'
project.reject_reason = rejectReason
await project.save()
const notification = new NotificationModel({
user_id: project?.creator_id,
project_post_id: project?._id,
type: 'reject-project',
title: 'رد درخواست',
description: `درخواست شما با عنوان: ${project?.title} رد شد، برای دیدن علت رد، لمس کنید `
})
await notification.save()
res.status(201).json({ message: 'پروژه با موفقیت رد شد' })
} catch (error) {
next(error)
}
}
module.exports = {
getProjects,
getProjectDetails,
acceptProject,
rejectProject
}

Some files were not shown because too many files have changed in this diff Show More