Compare commits
4 Commits
main
...
29d5830d54
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29d5830d54 | ||
|
|
2c6d8a4434 | ||
|
|
a7e4f68495 | ||
|
|
5719818b9d |
@@ -9,9 +9,7 @@
|
||||
// }
|
||||
// module.exports = startMongoDB
|
||||
|
||||
|
||||
const mongoose = require('mongoose')
|
||||
require('dotenv').config();
|
||||
mongoose.connection.on('error', (error) => {
|
||||
console.log('mongodb connection failed! ', error.message)
|
||||
})
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
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 });
|
||||
}
|
||||
};
|
||||
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 });
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,80 +1,72 @@
|
||||
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),
|
||||
createdAtIso: doc.createdAt ? new Date(doc.createdAt).toISOString() : undefined,
|
||||
expiresAt: doc.expiresAt ? new Date(doc.expiresAt).toISOString() : undefined,
|
||||
viewOnce: Boolean(doc.viewOnce),
|
||||
viewOnceLocked: Boolean(doc.viewOnceLocked),
|
||||
viewOnceExpired: Boolean(doc.viewOnceExpired),
|
||||
viewOnceOpenedAt: doc.viewOnceOpenedAt
|
||||
? new Date(doc.viewOnceOpenedAt).toISOString()
|
||||
: undefined,
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const MessageModel = require('../models/MessageModel')
|
||||
|
||||
const ALLOWED_SELF_DESTRUCT_SECONDS = [10, 30, 60, 300, 3600]
|
||||
|
||||
function parseSelfDestructSeconds(raw) {
|
||||
if (raw == null || raw === '' || raw === '0' || raw === 0) return null
|
||||
const n = parseInt(String(raw), 10)
|
||||
if (!Number.isFinite(n) || !ALLOWED_SELF_DESTRUCT_SECONDS.includes(n)) return null
|
||||
return n
|
||||
}
|
||||
|
||||
function buildExpiresAt(seconds) {
|
||||
if (!seconds) return undefined
|
||||
return new Date(Date.now() + seconds * 1000)
|
||||
}
|
||||
|
||||
function activeMessageFilter(baseFilter) {
|
||||
return {
|
||||
$and: [
|
||||
baseFilter,
|
||||
{
|
||||
$or: [
|
||||
{ expiresAt: { $exists: false } },
|
||||
{ expiresAt: null },
|
||||
{ expiresAt: { $gt: new Date() } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
async function removeMessageFiles(messages) {
|
||||
for (const msg of messages) {
|
||||
if (!msg.file) continue
|
||||
const relative = String(msg.file).replace(/^\//, '')
|
||||
const filePath = path.join(__dirname, '../storage', relative)
|
||||
if (fs.existsSync(filePath)) {
|
||||
await fs.remove(filePath).catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteMessagesAndNotify(io, messages, options = {}) {
|
||||
if (!messages?.length) return []
|
||||
|
||||
await removeMessageFiles(messages)
|
||||
const deletedIds = messages.map((m) => String(m._id))
|
||||
await MessageModel.deleteMany({ _id: { $in: deletedIds } })
|
||||
|
||||
if (io) {
|
||||
const byThread = new Map()
|
||||
messages.forEach((m) => {
|
||||
const s = String(m.senderId)
|
||||
const r = String(m.receiverId)
|
||||
const key = [s, r].sort().join(':')
|
||||
if (!byThread.has(key)) byThread.set(key, { s, r, ids: [] })
|
||||
byThread.get(key).ids.push(String(m._id))
|
||||
})
|
||||
|
||||
byThread.forEach(({ s, r, ids }) => {
|
||||
io.to(`chat:${s}:${r}`)
|
||||
.to(`chat:${r}:${s}`)
|
||||
.emit('messagesDeleted', {
|
||||
messageIds: ids,
|
||||
deletedBy: options.deletedBy,
|
||||
reason: options.reason || (options.deletedBy ? 'manual' : 'expired')
|
||||
})
|
||||
io.to(`user:${s}`).to(`user:${r}`).emit('chatListUpdate', { senderId: s, receiverId: r })
|
||||
})
|
||||
}
|
||||
|
||||
return deletedIds
|
||||
}
|
||||
|
||||
async function purgeExpiredMessages(io) {
|
||||
try {
|
||||
const expired = await MessageModel.find({ expiresAt: { $lte: new Date() } })
|
||||
if (!expired.length) return []
|
||||
return deleteMessagesAndNotify(io, expired)
|
||||
} catch (err) {
|
||||
console.error('purgeExpiredMessages error:', err)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
const scheduledTimers = new Map()
|
||||
|
||||
function scheduleMessageExpiry(io, message) {
|
||||
if (!message?.expiresAt || !message._id) return
|
||||
|
||||
const id = String(message._id)
|
||||
if (scheduledTimers.has(id)) {
|
||||
clearTimeout(scheduledTimers.get(id))
|
||||
}
|
||||
|
||||
const ms = new Date(message.expiresAt).getTime() - Date.now()
|
||||
if (ms <= 0) {
|
||||
MessageModel.findById(id)
|
||||
.then((doc) => doc && deleteMessagesAndNotify(io, [doc]))
|
||||
.catch(() => {})
|
||||
return
|
||||
}
|
||||
|
||||
const timer = setTimeout(async () => {
|
||||
scheduledTimers.delete(id)
|
||||
try {
|
||||
const doc = await MessageModel.findById(id)
|
||||
if (doc && doc.expiresAt && new Date(doc.expiresAt) <= new Date()) {
|
||||
await deleteMessagesAndNotify(io, [doc])
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('scheduleMessageExpiry error:', err)
|
||||
}
|
||||
}, ms)
|
||||
|
||||
scheduledTimers.set(id, timer)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ALLOWED_SELF_DESTRUCT_SECONDS,
|
||||
parseSelfDestructSeconds,
|
||||
buildExpiresAt,
|
||||
activeMessageFilter,
|
||||
purgeExpiredMessages,
|
||||
scheduleMessageExpiry,
|
||||
deleteMessagesAndNotify
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
const MessageModel = require('../models/MessageModel')
|
||||
const { deleteMessagesAndNotify } = require('./expiredMessages')
|
||||
|
||||
function parseViewOnce(raw) {
|
||||
if (raw == null || raw === '' || raw === false || raw === 'false' || raw === 0) {
|
||||
return false
|
||||
}
|
||||
return raw === true || raw === 'true' || raw === 1 || raw === '1'
|
||||
}
|
||||
|
||||
const VIEW_ONCE_MEDIA = new Set(['image', 'video', 'voice'])
|
||||
|
||||
function isViewOnceMediaType(fileType) {
|
||||
return VIEW_ONCE_MEDIA.has(fileType)
|
||||
}
|
||||
|
||||
/** Hide file URL for receiver until they open view-once media */
|
||||
function sanitizeMessageForViewer(message, viewerId) {
|
||||
const doc = message._doc ? { ...message._doc } : { ...message }
|
||||
const viewer = String(viewerId)
|
||||
const sender = String(doc.senderId)
|
||||
const receiver = String(doc.receiverId)
|
||||
|
||||
if (!doc.viewOnce || !isViewOnceMediaType(doc.fileType)) {
|
||||
return doc
|
||||
}
|
||||
|
||||
if (viewer === sender) {
|
||||
return doc
|
||||
}
|
||||
|
||||
if (viewer === receiver) {
|
||||
if (doc.viewOnceOpenedAt) {
|
||||
return {
|
||||
...doc,
|
||||
file: '',
|
||||
viewOnceExpired: true
|
||||
}
|
||||
}
|
||||
return {
|
||||
...doc,
|
||||
file: '',
|
||||
viewOnceLocked: true
|
||||
}
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
async function openViewOnceMessage(messageId, userId) {
|
||||
const msg = await MessageModel.findById(messageId)
|
||||
if (!msg || !msg.viewOnce) {
|
||||
return { error: 'پیام یافت نشد', status: 404 }
|
||||
}
|
||||
if (String(msg.receiverId) !== String(userId)) {
|
||||
return { error: 'دسترسی مجاز نیست', status: 403 }
|
||||
}
|
||||
if (msg.viewOnceOpenedAt) {
|
||||
return { error: 'این پیام قبلاً باز شده است', status: 410 }
|
||||
}
|
||||
|
||||
msg.viewOnceOpenedAt = new Date()
|
||||
await msg.save()
|
||||
|
||||
return {
|
||||
message: msg.toObject(),
|
||||
file: msg.file,
|
||||
fileType: msg.fileType
|
||||
}
|
||||
}
|
||||
|
||||
async function completeViewOnceMessage(io, messageId, userId) {
|
||||
const msg = await MessageModel.findById(messageId)
|
||||
if (!msg || !msg.viewOnce) {
|
||||
return { error: 'پیام یافت نشد', status: 404 }
|
||||
}
|
||||
|
||||
const uid = String(userId)
|
||||
const isReceiver = String(msg.receiverId) === uid
|
||||
const isSender = String(msg.senderId) === uid
|
||||
|
||||
if (!isReceiver && !isSender) {
|
||||
return { error: 'دسترسی مجاز نیست', status: 403 }
|
||||
}
|
||||
|
||||
if (isReceiver && !msg.viewOnceOpenedAt) {
|
||||
return { error: 'پیام هنوز باز نشده', status: 400 }
|
||||
}
|
||||
|
||||
await deleteMessagesAndNotify(io, [msg], {
|
||||
deletedBy: userId,
|
||||
reason: 'viewOnce'
|
||||
})
|
||||
|
||||
return { deletedIds: [String(msg._id)] }
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseViewOnce,
|
||||
isViewOnceMediaType,
|
||||
sanitizeMessageForViewer,
|
||||
openViewOnceMessage,
|
||||
completeViewOnceMessage
|
||||
}
|
||||
@@ -1,357 +1,357 @@
|
||||
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}`)
|
||||
})
|
||||
}
|
||||
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}`)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module.exports = (app) => {
|
||||
app.use((req, res, next) => {
|
||||
res.status(404).send({
|
||||
code: 'Not Found',
|
||||
status: 404,
|
||||
message: 'requested resource could not be found!'
|
||||
})
|
||||
})
|
||||
}
|
||||
module.exports = (app) => {
|
||||
app.use((req, res, next) => {
|
||||
res.status(404).send({
|
||||
code: 'Not Found',
|
||||
status: 404,
|
||||
message: 'requested resource could not be found!'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
const AdminModel = require('../models/AdminModel')
|
||||
const jwt = require('jsonwebtoken')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'you are not authorized!'
|
||||
})
|
||||
}
|
||||
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
if (!token) return res.status(401).send('Access Denied')
|
||||
let decodedToken
|
||||
try {
|
||||
decodedToken = jwt.verify(token, process.env.APP_SECRET)
|
||||
} catch (err) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'Your token is not valid!',
|
||||
error: err.message
|
||||
})
|
||||
}
|
||||
if (!decodedToken) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'Your token is not valid!'
|
||||
})
|
||||
}
|
||||
try {
|
||||
// پیدا کردن کاربر با استفاده از اطلاعات موجود در توکن
|
||||
const admin = await AdminModel.findOne({ _id: decodedToken.id })
|
||||
if (!admin) {
|
||||
return res.status(403).send({
|
||||
status: 'error',
|
||||
code: 403,
|
||||
message: 'Access denied!'
|
||||
})
|
||||
}
|
||||
|
||||
// ادامه مسیر در صورتی که کاربر ادمین باشد
|
||||
req.admin = admin
|
||||
next()
|
||||
} catch (error) {
|
||||
return res.status(500).send({
|
||||
status: 'error',
|
||||
code: 500,
|
||||
message: 'Internal Server Error',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
const AdminModel = require('../models/AdminModel')
|
||||
const jwt = require('jsonwebtoken')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'you are not authorized!'
|
||||
})
|
||||
}
|
||||
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
if (!token) return res.status(401).send('Access Denied')
|
||||
let decodedToken
|
||||
try {
|
||||
decodedToken = jwt.verify(token, process.env.APP_SECRET)
|
||||
} catch (err) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'Your token is not valid!',
|
||||
error: err.message
|
||||
})
|
||||
}
|
||||
if (!decodedToken) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'Your token is not valid!'
|
||||
})
|
||||
}
|
||||
try {
|
||||
// پیدا کردن کاربر با استفاده از اطلاعات موجود در توکن
|
||||
const admin = await AdminModel.findOne({ _id: decodedToken.id })
|
||||
if (!admin) {
|
||||
return res.status(403).send({
|
||||
status: 'error',
|
||||
code: 403,
|
||||
message: 'Access denied!'
|
||||
})
|
||||
}
|
||||
|
||||
// ادامه مسیر در صورتی که کاربر ادمین باشد
|
||||
req.admin = admin
|
||||
next()
|
||||
} catch (error) {
|
||||
return res.status(500).send({
|
||||
status: 'error',
|
||||
code: 500,
|
||||
message: 'Internal Server Error',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
const TokenService = require('../services/TokenService')
|
||||
module.exports = (req, res, next) => {
|
||||
console.log(1);
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'you are not authorized!'
|
||||
})
|
||||
}
|
||||
const [, tokenValue] = req.headers.authorization.split(' ')
|
||||
const token = TokenService.verify(tokenValue)
|
||||
if (!token) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'your token is not valid!'
|
||||
})
|
||||
}
|
||||
next()
|
||||
}
|
||||
const TokenService = require('../services/TokenService')
|
||||
module.exports = (req, res, next) => {
|
||||
console.log(1);
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'you are not authorized!'
|
||||
})
|
||||
}
|
||||
const [, tokenValue] = req.headers.authorization.split(' ')
|
||||
const token = TokenService.verify(tokenValue)
|
||||
if (!token) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'your token is not valid!'
|
||||
})
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
@@ -1,48 +1,47 @@
|
||||
const UserModel = require('../models/UserModel')
|
||||
const jwt = require('jsonwebtoken')
|
||||
const {
|
||||
expireBlockSuspensionIfNeeded,
|
||||
getBlockedAccountResponse
|
||||
} = require('../utils/blockSuspension')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
try {
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'You are not authorized!'
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if (!decodedToken) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'Your token is not valid!'
|
||||
})
|
||||
}
|
||||
|
||||
let user = await UserModel.findById(decodedToken.id)
|
||||
user = await expireBlockSuspensionIfNeeded(user)
|
||||
|
||||
const blockResponse = getBlockedAccountResponse(user)
|
||||
if (blockResponse) {
|
||||
return res.status(403).send(blockResponse)
|
||||
}
|
||||
|
||||
req.user = user
|
||||
next()
|
||||
} catch (error) {
|
||||
console.error('Error in blockCheck middleware:', error)
|
||||
res.status(500).send({
|
||||
status: 'error',
|
||||
code: 500,
|
||||
message: 'Server error'
|
||||
})
|
||||
}
|
||||
}
|
||||
const UserModel = require('../models/UserModel') // مسیر درست به مدل کاربر
|
||||
const jwt = require('jsonwebtoken')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
console.log(2);
|
||||
try {
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'You are not authorized!'
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if (!decodedToken) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'Your token is not valid!'
|
||||
})
|
||||
}
|
||||
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (user.block_status === true) {
|
||||
return res.status(403).send({
|
||||
status: 'error',
|
||||
code: 403,
|
||||
message: 'حساب کاربری شما مسدود شده است، برای اطلاعات بیشتر با پشتیبانی تماس بگیرید!',
|
||||
type: 'block'
|
||||
})
|
||||
}
|
||||
|
||||
req.user = user // کاربر را به درخواست اضافه کنید تا در سایر middleware ها و کنترلرها استفاده شود.
|
||||
next()
|
||||
} catch (error) {
|
||||
console.error('Error in blockCheck middleware:', error)
|
||||
res.status(500).send({
|
||||
status: 'error',
|
||||
code: 500,
|
||||
message: 'Server error'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module.exports = (app) => {
|
||||
app.use((error, req, res, next) => {
|
||||
const status = error.status || 500
|
||||
res.status(500).send({
|
||||
code: 'Eception',
|
||||
status,
|
||||
en_message: error.message,
|
||||
message: 'خطایی در عملیات مورد نظر رخ داده است.'
|
||||
})
|
||||
})
|
||||
}
|
||||
module.exports = (app) => {
|
||||
app.use((error, req, res, next) => {
|
||||
const status = error.status || 500
|
||||
res.status(500).send({
|
||||
code: 'Eception',
|
||||
status,
|
||||
en_message: error.message,
|
||||
message: 'خطایی در عملیات مورد نظر رخ داده است.'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
const bodyParser = require('body-parser')
|
||||
const cors = require('cors')
|
||||
const formData = require('express-form-data')
|
||||
const session = require('express-session')
|
||||
const lastOnline = require('./lastOnline')
|
||||
module.exports = (app) => {
|
||||
app.use(cors()) // develop
|
||||
app.use(formData.parse())
|
||||
app.use(bodyParser.json())
|
||||
app.use(bodyParser.urlencoded({ extended: true }))
|
||||
app.use(session({
|
||||
secret: 'your_secret_key', // کلید مخفی برای امنیت جلسات
|
||||
resave: false,
|
||||
saveUninitialized: false
|
||||
}))
|
||||
app.use(lastOnline)
|
||||
}
|
||||
const bodyParser = require('body-parser')
|
||||
const cors = require('cors')
|
||||
const formData = require('express-form-data')
|
||||
const session = require('express-session')
|
||||
const lastOnline = require('./lastOnline')
|
||||
module.exports = (app) => {
|
||||
app.use(cors()) // develop
|
||||
app.use(formData.parse())
|
||||
app.use(bodyParser.json())
|
||||
app.use(bodyParser.urlencoded({ extended: true }))
|
||||
app.use(session({
|
||||
secret: 'your_secret_key', // کلید مخفی برای امنیت جلسات
|
||||
resave: false,
|
||||
saveUninitialized: false
|
||||
}))
|
||||
app.use(lastOnline)
|
||||
}
|
||||
|
||||
@@ -1,53 +1,52 @@
|
||||
const UserModel = require('../models/UserModel') // مسیر درست به مدل کاربر
|
||||
const jwt = require('jsonwebtoken')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
console.log(3);
|
||||
try {
|
||||
console.log(31);
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'You are not authorized!'
|
||||
})
|
||||
}
|
||||
console.log(32);
|
||||
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)
|
||||
|
||||
if (!decodedToken) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'Your token is not valid!'
|
||||
})
|
||||
}
|
||||
console.log(33);
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (!user.user_name ||
|
||||
!user.first_name ||
|
||||
!user.last_name ||
|
||||
!user.user_type ||
|
||||
!user.password) {
|
||||
return res.status(403).send({
|
||||
status: 'error',
|
||||
code: 403,
|
||||
message: 'لطفا از بخش تنظیمات، ثبت نام خود را کامل کنید',
|
||||
type: 'not_register'
|
||||
})
|
||||
}
|
||||
console.log(34);
|
||||
req.user = user // کاربر را به درخواست اضافه کنید تا در سایر middleware ها و کنترلرها استفاده شود.
|
||||
next()
|
||||
} catch (error) {
|
||||
console.log(35);
|
||||
console.error('not_register middleware:', error)
|
||||
res.status(500).send({
|
||||
status: 'error',
|
||||
code: 500,
|
||||
message: 'Server error'
|
||||
})
|
||||
}
|
||||
}
|
||||
const UserModel = require('../models/UserModel') // مسیر درست به مدل کاربر
|
||||
const jwt = require('jsonwebtoken')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
console.log(3);
|
||||
try {
|
||||
console.log(31);
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'You are not authorized!'
|
||||
})
|
||||
}
|
||||
console.log(32);
|
||||
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)
|
||||
|
||||
if (!decodedToken) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'Your token is not valid!'
|
||||
})
|
||||
}
|
||||
console.log(33);
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (!user.user_name ||
|
||||
!user.first_name ||
|
||||
!user.user_type ||
|
||||
!user.password) {
|
||||
return res.status(403).send({
|
||||
status: 'error',
|
||||
code: 403,
|
||||
message: 'لطفا از بخش تنظیمات، ثبت نام خود را کامل کنید',
|
||||
type: 'not_register'
|
||||
})
|
||||
}
|
||||
console.log(34);
|
||||
req.user = user // کاربر را به درخواست اضافه کنید تا در سایر middleware ها و کنترلرها استفاده شود.
|
||||
next()
|
||||
} catch (error) {
|
||||
console.log(35);
|
||||
console.error('not_register middleware:', error)
|
||||
res.status(500).send({
|
||||
status: 'error',
|
||||
code: 500,
|
||||
message: 'Server error'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,49 @@
|
||||
const UserModel = require('../models/UserModel') // مسیر درست به مدل کاربر
|
||||
const jwt = require('jsonwebtoken')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
try {
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'You are not authorized!'
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if (!decodedToken) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'Your token is not valid!'
|
||||
})
|
||||
}
|
||||
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (!user.user_name ||
|
||||
!user.first_name ||
|
||||
!user.last_name ||
|
||||
!user.password) {
|
||||
return res.status(403).send({
|
||||
status: 'error',
|
||||
code: 403,
|
||||
message: 'لطفا از بخش تنظیمات، ثبت نام خود را کامل کنید',
|
||||
type: 'not_register'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
req.user = user // کاربر را به درخواست اضافه کنید تا در سایر middleware ها و کنترلرها استفاده شود.
|
||||
next()
|
||||
} catch (error) {
|
||||
console.error('not_register middleware:', error)
|
||||
res.status(500).send({
|
||||
status: 'error',
|
||||
code: 500,
|
||||
message: 'Server error'
|
||||
})
|
||||
}
|
||||
}
|
||||
const UserModel = require('../models/UserModel') // مسیر درست به مدل کاربر
|
||||
const jwt = require('jsonwebtoken')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
try {
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'You are not authorized!'
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if (!decodedToken) {
|
||||
return res.status(401).send({
|
||||
status: 'error',
|
||||
code: 401,
|
||||
message: 'Your token is not valid!'
|
||||
})
|
||||
}
|
||||
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (!user.user_name ||
|
||||
!user.first_name ||
|
||||
!user.password) {
|
||||
return res.status(403).send({
|
||||
status: 'error',
|
||||
code: 403,
|
||||
message: 'لطفا از بخش تنظیمات، ثبت نام خود را کامل کنید',
|
||||
type: 'not_register'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
req.user = user // کاربر را به درخواست اضافه کنید تا در سایر middleware ها و کنترلرها استفاده شود.
|
||||
next()
|
||||
} catch (error) {
|
||||
console.error('not_register middleware:', error)
|
||||
res.status(500).send({
|
||||
status: 'error',
|
||||
code: 500,
|
||||
message: 'Server error'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
const jwt = require('jsonwebtoken')
|
||||
const UserModel = require('../models/UserModel')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
if (req.header && req.header('Authorization')) {
|
||||
const token = await req.header('Authorization').split(' ')[1]
|
||||
const decodedToken = await jwt.verify(token, process.env.APP_SECRET)
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (user) {
|
||||
try {
|
||||
user.last_online = new Date()
|
||||
await user.save() // ذخیره تغییرات در دیتابیس
|
||||
} catch (error) {
|
||||
// بررسی و پردازش خطاها
|
||||
console.error('Error updating last online time:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
const jwt = require('jsonwebtoken')
|
||||
const UserModel = require('../models/UserModel')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
if (req.header && req.header('Authorization')) {
|
||||
const token = await req.header('Authorization').split(' ')[1]
|
||||
const decodedToken = await jwt.verify(token, process.env.APP_SECRET)
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (user) {
|
||||
try {
|
||||
user.last_online = new Date()
|
||||
await user.save() // ذخیره تغییرات در دیتابیس
|
||||
} catch (error) {
|
||||
// بررسی و پردازش خطاها
|
||||
console.error('Error updating last online time:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
const multer = require('multer');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
const uploadDir = path.join(__dirname, '../storage/posts');
|
||||
fs.ensureDirSync(uploadDir); // مطمئن میشه مسیر وجود داره
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => cb(null, uploadDir),
|
||||
filename: (req, file, cb) => {
|
||||
const ext = path.extname(file.originalname);
|
||||
const name = file.originalname.replace(ext, '').replace(/\s/g, '_');
|
||||
cb(null, `${Date.now()}-${name}${ext}`);
|
||||
}
|
||||
});
|
||||
|
||||
const fileFilter = (req, file, cb) => {
|
||||
if (!file.mimetype.startsWith('image/')) return cb(new Error('فقط تصاویر مجاز است'));
|
||||
cb(null, true);
|
||||
};
|
||||
|
||||
const upload = multer({ storage, fileFilter, limits: { files: 10 } }); // حداکثر ۱۰ فایل
|
||||
|
||||
module.exports = upload;
|
||||
const multer = require('multer');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
const uploadDir = path.join(__dirname, '../storage/posts');
|
||||
fs.ensureDirSync(uploadDir); // مطمئن میشه مسیر وجود داره
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => cb(null, uploadDir),
|
||||
filename: (req, file, cb) => {
|
||||
const ext = path.extname(file.originalname);
|
||||
const name = file.originalname.replace(ext, '').replace(/\s/g, '_');
|
||||
cb(null, `${Date.now()}-${name}${ext}`);
|
||||
}
|
||||
});
|
||||
|
||||
const fileFilter = (req, file, cb) => {
|
||||
if (!file.mimetype.startsWith('image/')) return cb(new Error('فقط تصاویر مجاز است'));
|
||||
cb(null, true);
|
||||
};
|
||||
|
||||
const upload = multer({ storage, fileFilter, limits: { files: 10 } }); // حداکثر ۱۰ فایل
|
||||
|
||||
module.exports = upload;
|
||||
|
||||
@@ -1,190 +1,190 @@
|
||||
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);
|
||||
|
||||
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);
|
||||
@@ -1,71 +1,71 @@
|
||||
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);
|
||||
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;
|
||||
@@ -1,25 +1,25 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,230 +1,230 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,113 +1,113 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,55 +1,54 @@
|
||||
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: false,
|
||||
default: null
|
||||
},
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -1,79 +1,79 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
const mongoose = require('mongoose')
|
||||
const timestamp = require('mongoose-timestamp')
|
||||
|
||||
const exploreInteractionSchema = new mongoose.Schema({
|
||||
viewerId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
required: true,
|
||||
index: true
|
||||
},
|
||||
targetType: {
|
||||
type: String,
|
||||
enum: ['post', 'profile', 'academy'],
|
||||
required: true
|
||||
},
|
||||
targetId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
required: true
|
||||
},
|
||||
authorId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
default: null
|
||||
},
|
||||
contentType: {
|
||||
type: String,
|
||||
enum: ['image', 'video', 'academy'],
|
||||
default: 'image'
|
||||
},
|
||||
action: {
|
||||
type: String,
|
||||
enum: [
|
||||
'view',
|
||||
'profile_visit',
|
||||
'share',
|
||||
'like',
|
||||
'comment',
|
||||
'offer',
|
||||
'watch_complete',
|
||||
'skip',
|
||||
'dwell'
|
||||
],
|
||||
default: 'view'
|
||||
},
|
||||
dwellMs: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
exploreInteractionSchema.index({ viewerId: 1, createdAt: -1 })
|
||||
exploreInteractionSchema.plugin(timestamp)
|
||||
|
||||
module.exports = mongoose.model('ExploreInteraction', exploreInteractionSchema)
|
||||
@@ -1,15 +1,15 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,66 +1,51 @@
|
||||
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
|
||||
},
|
||||
/** Auto-delete after this time (self-destruct messages) */
|
||||
expiresAt: {
|
||||
type: Date,
|
||||
required: false,
|
||||
index: true
|
||||
},
|
||||
/** View-once photo / video / voice */
|
||||
viewOnce: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
viewOnceOpenedAt: {
|
||||
type: Date,
|
||||
required: false
|
||||
}
|
||||
})
|
||||
|
||||
const MessageModel = mongoose.model('Message', messageSchema)
|
||||
|
||||
module.exports = MessageModel
|
||||
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
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
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);
|
||||
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;
|
||||
@@ -1,186 +1,186 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
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 }
|
||||
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 }
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
const mongoose = require('mongoose')
|
||||
const timestamp = require('mongoose-timestamp')
|
||||
|
||||
const storySchema = new mongoose.Schema({
|
||||
user_id: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User',
|
||||
required: true,
|
||||
index: true
|
||||
},
|
||||
media_path: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
media_type: {
|
||||
type: String,
|
||||
enum: ['image', 'video'],
|
||||
required: true
|
||||
},
|
||||
expires_at: {
|
||||
type: Date,
|
||||
required: true,
|
||||
index: true
|
||||
},
|
||||
viewers: [{
|
||||
user_id: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'User'
|
||||
},
|
||||
viewed_at: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
}
|
||||
}]
|
||||
})
|
||||
|
||||
storySchema.index({ expires_at: 1 }, { expireAfterSeconds: 0 })
|
||||
|
||||
storySchema.plugin(timestamp)
|
||||
|
||||
module.exports = mongoose.model('Story', storySchema)
|
||||
@@ -1,15 +1,15 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,391 +1,381 @@
|
||||
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
|
||||
},
|
||||
otpSentAt: {
|
||||
type: Date,
|
||||
required: false,
|
||||
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
|
||||
},
|
||||
blocked_until: {
|
||||
type: Date,
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
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
|
||||
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
|
||||
|
||||
@@ -1,423 +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 {
|
||||
resolveRatingForComment,
|
||||
recalculateUserRating
|
||||
} = require('../../../utils/commentRating')
|
||||
|
||||
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 || !user_id) {
|
||||
return res.status(400).send({ message: 'All fields are required' })
|
||||
}
|
||||
|
||||
const { ratingValue, isNewRating, requiresRating } = await resolveRatingForComment({
|
||||
creatorId: userId,
|
||||
targetUserId: user_id,
|
||||
rate
|
||||
})
|
||||
|
||||
if (requiresRating) {
|
||||
return res.status(400).send({ message: 'Rating must be between 1 and 5' })
|
||||
}
|
||||
|
||||
const newComment = new CommentModel({
|
||||
user: user_id,
|
||||
offer: offerId,
|
||||
creator: userId,
|
||||
rating: ratingValue,
|
||||
comment,
|
||||
comment_for: 'user',
|
||||
status: 'pending'
|
||||
})
|
||||
await newComment.save()
|
||||
|
||||
if (isNewRating) {
|
||||
await recalculateUserRating(user_id)
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'نظر شما با موفقیت ثبت شد' })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getUserOffers,
|
||||
getOfferTypes,
|
||||
handleSuccessfulOfferPayment,
|
||||
handleFailedOfferPayment,
|
||||
updateOfferStatus,
|
||||
createOfferComment
|
||||
}
|
||||
@@ -1,389 +0,0 @@
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const academyController = require("../../../controllers/application/academy/academyControllers");
|
||||
const multer = require("multer");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
// const academyCategoryController = require("../../../controllers/application/academy/academyCategoryController");
|
||||
|
||||
// ==========================================
|
||||
// ⚙️ تنظیمات اولیه و دایرکتوریها
|
||||
// ==========================================
|
||||
|
||||
// اطمینان از وجود دایرکتوریها
|
||||
const ensureDir = (dir) => {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
console.log("DEBUG: Created directory:", dir);
|
||||
}
|
||||
};
|
||||
|
||||
const BASE_DIR = process.cwd();
|
||||
const courseVideoDir = path.join(BASE_DIR, "storage", "courses", "videos");
|
||||
const courseImageDir = path.join(BASE_DIR, "storage", "courses", "images");
|
||||
|
||||
console.log("DEBUG: BASE_DIR:", BASE_DIR);
|
||||
console.log("DEBUG: courseVideoDir:", courseVideoDir);
|
||||
console.log("DEBUG: courseImageDir:", courseImageDir);
|
||||
|
||||
ensureDir(courseVideoDir);
|
||||
ensureDir(courseImageDir);
|
||||
|
||||
// ==========================================
|
||||
// 📁 تنظیمات آپلود فایل با Multer
|
||||
// ==========================================
|
||||
|
||||
// تنظیمات ذخیرهسازی فایلها
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
console.log("DEBUG: Multer destination - fieldname:", file.fieldname);
|
||||
// استفاده از دایرکتوری موقت برای آپلود اولیه
|
||||
const tempDir = "/tmp/modstagram-uploads";
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
}
|
||||
cb(null, tempDir);
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
|
||||
const filename = uniqueSuffix + path.extname(file.originalname);
|
||||
console.log("DEBUG: Generated filename:", filename);
|
||||
cb(null, filename);
|
||||
},
|
||||
});
|
||||
|
||||
// تنظیمات Multer با محدودیت حجم 500 مگابایت
|
||||
const upload = multer({
|
||||
storage: storage,
|
||||
limits: {
|
||||
fileSize: 500 * 1024 * 1024, // 500 MB
|
||||
},
|
||||
});
|
||||
|
||||
// ==========================================
|
||||
// 🏢 مسیرهای مربوط به آکادمی
|
||||
// ==========================================
|
||||
|
||||
// دریافت اطلاعات آکادمی کاربر جاری
|
||||
router.get("/", academyController.findAcademy);
|
||||
|
||||
// یافتن آکادمی با شناسه
|
||||
router.post("/findAcademyById", academyController.findAcademyById);
|
||||
|
||||
// تکمیل پروفایل آکادمی
|
||||
router.post("/academy/profile", academyController.academyProfile);
|
||||
|
||||
// ==========================================
|
||||
// 📚 مسیرهای مدیریت دورهها (Courses)
|
||||
// ==========================================
|
||||
|
||||
// ایجاد دوره جدید
|
||||
router.post("/academy/creat/course", academyController.creatCourse);
|
||||
|
||||
// ویرایش دوره
|
||||
router.patch("/academy/update/course", academyController.updateCourse);
|
||||
|
||||
// حذف دوره (تغییر وضعیت به reject)
|
||||
router.delete("/academy/delete/course", academyController.deleteCourse);
|
||||
|
||||
// دریافت لیست دورههای آکادمی جاری
|
||||
router.get("/academy/get/course", academyController.getCourse);
|
||||
|
||||
// دریافت لیست همه دورهها با فیلتر و صفحهبندی
|
||||
router.get("/academy/get/curses", academyController.getCourses);
|
||||
|
||||
// محتوای رایگان آموزشگاه برای اکسپلور
|
||||
router.get(
|
||||
"/academy/explore/free-content",
|
||||
academyController.getFreeAcademyExploreContent
|
||||
);
|
||||
|
||||
// دریافت دورههای یک آکادمی خاص
|
||||
router.get("/academy/get/getAcademyCourse/:Id", academyController.getAcademyCourse);
|
||||
|
||||
// ==========================================
|
||||
// 💳 مسیرهای پرداخت و مالی
|
||||
// ==========================================
|
||||
|
||||
// پرداخت برای اشتراک پرو
|
||||
router.post("/academy/course/payment", academyController.proPayment);
|
||||
|
||||
// کالبک درگاه پرداخت زرینپال (اشتراک پرو)
|
||||
router.get("/academy/course/pro", academyController.handleCoursePaymentCallback);
|
||||
|
||||
// پرداخت برای خرید دوره
|
||||
router.post("/academy/course/payment-web", academyController.coursePayment);
|
||||
|
||||
// کالبک درگاه پرداخت زرینپال (خرید دوره)
|
||||
router.get("/academy/course/payment/verify", academyController.CoursePaymentCallback);
|
||||
|
||||
// ==========================================
|
||||
// 🎬 مسیرهای محتوای دوره (ویدیوها و فایلها)
|
||||
// ==========================================
|
||||
|
||||
// دریافت محتوای دوره (ویدیوها)
|
||||
router.get("/academy/course/content", academyController.getCourseContent);
|
||||
|
||||
// حذف ویدیو از دوره
|
||||
router.delete("/academy/course/content/video", academyController.deleteCourseVideo);
|
||||
|
||||
// ==========================================
|
||||
// 📹 مسیرهای آپلود ویدیو (۳ روش مختلف)
|
||||
// ==========================================
|
||||
|
||||
// روش 1: آپلود معمولی با Multipart (با استفاده از Multer)
|
||||
router.post(
|
||||
"/academy/course/creat/video",
|
||||
(req, res, next) => {
|
||||
console.log("===== STEP 1: Route hit =====");
|
||||
console.log("Content-Type:", req.headers["content-type"]);
|
||||
next();
|
||||
},
|
||||
(req, res, next) => {
|
||||
console.log("===== STEP 2: Before multer =====");
|
||||
next();
|
||||
},
|
||||
(req, res, next) => {
|
||||
// اجرای manual multer با try-catch
|
||||
upload.single("video")(req, res, (err) => {
|
||||
if (err) {
|
||||
console.log("===== MULTER ERROR =====");
|
||||
console.log("Error name:", err.name);
|
||||
console.log("Error message:", err.message);
|
||||
console.log("Full error:", err);
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `خطا در آپلود: ${err.message}`,
|
||||
error: err.toString(),
|
||||
});
|
||||
}
|
||||
console.log("===== STEP 3: Multer completed successfully =====");
|
||||
next();
|
||||
});
|
||||
},
|
||||
(req, res, next) => {
|
||||
console.log("===== STEP 4: After multer =====");
|
||||
console.log("req.file:", req.file);
|
||||
console.log("req.body:", req.body);
|
||||
|
||||
if (!req.file) {
|
||||
console.log("ERROR: No file in req.file!");
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "فایلی دریافت نشد",
|
||||
});
|
||||
}
|
||||
next();
|
||||
},
|
||||
academyController.creatCourseVideo
|
||||
);
|
||||
|
||||
// روش 2: آپلود با Base64 (برای فایلهای کوچک)
|
||||
router.post(
|
||||
"/academy/course/creat/video-base64",
|
||||
academyController.creatCourseVideoBase64
|
||||
);
|
||||
|
||||
// روش 3: آپلود تکهتکه (Chunk Upload) - مناسب برای فایلهای بزرگ
|
||||
// دریافت هر تکه از ویدیو
|
||||
router.post(
|
||||
"/academy/course/creat/video-chunk",
|
||||
academyController.creatCourseVideoChunk
|
||||
);
|
||||
|
||||
// ادغام تکهها بعد از اتمام آپلود
|
||||
router.post(
|
||||
"/academy/course/creat/video-chunk/merge",
|
||||
academyController.mergeVideoChunks
|
||||
);
|
||||
|
||||
// بررسی وضعیت آپلود (برای قابلیت Resume)
|
||||
router.get("/academy/course/upload-status", academyController.getUploadStatus);
|
||||
|
||||
// ==========================================
|
||||
// ❤️ مسیرهای لایک و تعامل
|
||||
// ==========================================
|
||||
|
||||
// لایک کردن محتوای دوره
|
||||
router.post("/academy/course/like", academyController.likeCourseContent);
|
||||
|
||||
// حذف لایک (دیسلایک)
|
||||
router.post("/academy/course/dislike", academyController.dontLikeCourseContent);
|
||||
|
||||
// بررسی وضعیت لایک کاربر
|
||||
router.post("/academy/course/is-like", academyController.isLikeCourseContent);
|
||||
|
||||
// دریافت تعداد لایکهای یک دوره
|
||||
router.get(
|
||||
"/academy/course/likes-count/:courseId",
|
||||
academyController.getCourseLikesCount
|
||||
);
|
||||
|
||||
// دریافت لیست دورههای لایک شده توسط کاربر
|
||||
router.get(
|
||||
"/academy/user/liked-courses",
|
||||
academyController.getUserLikedCourses
|
||||
);
|
||||
|
||||
// ==========================================
|
||||
// 💬 مسیرهای کامنت و نظرات
|
||||
// ==========================================
|
||||
|
||||
// دریافت تعداد کل کامنتهای یک دوره
|
||||
router.get(
|
||||
"/course/:courseId/comments/count",
|
||||
academyController.getTotalCommentsCount
|
||||
);
|
||||
|
||||
// دریافت لیست کامنتهای یک دوره
|
||||
router.get("/course/:courseId/comments", academyController.getCourseComments);
|
||||
|
||||
// ایجاد کامنت جدید
|
||||
router.post("/comment/create", academyController.createComment);
|
||||
|
||||
// تغییر وضعیت کامنت (تایید/رد)
|
||||
router.patch(
|
||||
"/comment/:commentId/status",
|
||||
academyController.updateCommentStatus
|
||||
);
|
||||
|
||||
// حذف کامنت
|
||||
router.delete("/comment/:commentId/delete", academyController.deleteComment);
|
||||
|
||||
// ==========================================
|
||||
// 🛒 مسیرهای دورههای خریداری شده
|
||||
// ==========================================
|
||||
|
||||
// دریافت دورههای خریداری شده توسط کاربر جاری
|
||||
router.get(
|
||||
"/course/getUserPurchasedCourses",
|
||||
academyController.getUserPurchasedCourses
|
||||
);
|
||||
|
||||
// دریافت دورههای خریداری شده (با populate - روش بهینه)
|
||||
router.get(
|
||||
"/course/getUserPurchasedCoursesWithPopulate",
|
||||
academyController.getUserPurchasedCoursesWithPopulate
|
||||
);
|
||||
|
||||
// دریافت همه پرداختهای آکادمی
|
||||
router.get(
|
||||
"/course/getAllAcademyPayments",
|
||||
academyController.getAllAcademyPayments
|
||||
);
|
||||
|
||||
// بررسی خرید دوره توسط کاربر
|
||||
router.get(
|
||||
"/course/checkCoursePurchase/:courseId",
|
||||
academyController.checkCoursePurchase
|
||||
);
|
||||
|
||||
// ==========================================
|
||||
// 📊 مسیرهای آمار و گزارشات (STATIC)
|
||||
// ==========================================
|
||||
|
||||
// آمار پرداختها
|
||||
router.get("/payments/stats", academyController.getPaymentStats);
|
||||
|
||||
// آمار دورهها
|
||||
router.get("/courses/stats", academyController.getCourseStats);
|
||||
|
||||
// آمار دورههای خریداری شده
|
||||
router.get("/purchased/stats", academyController.getPurchasedStats);
|
||||
|
||||
// آمار آکادمیها
|
||||
router.get("/academies/stats", academyController.getAcademyStats);
|
||||
|
||||
// ==========================================
|
||||
// ⚙️ مسیرهای تنظیمات (Settings)
|
||||
// ==========================================
|
||||
|
||||
// دریافت همه تنظیمات
|
||||
router.get("/settings/all", academyController.getAllSettings);
|
||||
router.get("/settings", academyController.getAllSettings);
|
||||
|
||||
// بهروزرسانی همه تنظیمات
|
||||
router.put("/settings/all", academyController.updateAllSettings);
|
||||
router.put("/settings", academyController.updateAllSettings);
|
||||
|
||||
// دریافت و ویرایش قیمت دورهها
|
||||
router.get("/settings/course-prices", academyController.getCoursePrices);
|
||||
router.put("/settings/course-price", academyController.updateCoursePrice);
|
||||
|
||||
// دریافت و ویرایش مالیات
|
||||
router.get("/settings/tax", academyController.getTax);
|
||||
router.put("/settings/tax", academyController.updateTax);
|
||||
|
||||
// ==========================================
|
||||
// 🏷️ مسیرهای دستهبندی آکادمی (Categories)
|
||||
// ==========================================
|
||||
|
||||
// دریافت لیست دستهبندیها (با فیلتر و صفحهبندی)
|
||||
router.get('/categories', academyController.getAll);
|
||||
|
||||
// دریافت درخت دستهبندی (ساختار سلسلهمراتبی)
|
||||
router.get('/categories/tree', academyController.getTree);
|
||||
|
||||
// دریافت دستهبندیهای ویژه (برای نمایش در هوم پیج)
|
||||
router.get('/categories/featured', academyController.getFeatured);
|
||||
|
||||
// دریافت دستهبندی با اسلاگ (برای سئو و لینکهای زیبا)
|
||||
router.get('/categories/slug/:slug', academyController.getBySlug);
|
||||
|
||||
// دریافت یک دستهبندی با شناسه
|
||||
router.get('/categories/:id', academyController.getOne);
|
||||
|
||||
// ایجاد دستهبندی جدید
|
||||
router.post('/categories', academyController.create);
|
||||
|
||||
// ویرایش دستهبندی (هم PUT و هم PATCH پشتیبانی میشود)
|
||||
router.put('/categories/:id', academyController.update);
|
||||
router.patch('/categories/:id', academyController.update);
|
||||
|
||||
// حذف دستهبندی (سافت دیلیت - فقط غیرفعال میشود)
|
||||
router.delete('/categories/:id', academyController.remove);
|
||||
|
||||
// حذف فیزیکی دستهبندی (به همراه query string permanent=true)
|
||||
router.delete('/categories/:id/permanent', (req, res) => {
|
||||
req.query.permanent = 'true';
|
||||
academyController.remove(req, res);
|
||||
});
|
||||
|
||||
// تغییر وضعیت دستهبندی (فعال/غیرفعال)
|
||||
router.patch('/categories/:id/status', academyController.toggleStatus);
|
||||
|
||||
// افزایش تعداد دورههای دستهبندی (هر بار یک دوره جدید اضافه میشود)
|
||||
router.post('/categories/:id/increment-count', academyController.incrementCourseCount);
|
||||
|
||||
// ==========================================
|
||||
// 👑 مسیرهای مدیریتی (پنل ادمین)
|
||||
// ==========================================
|
||||
|
||||
// ------ مدیریت پرداختها ------
|
||||
router.get("/payments", academyController.getAllPayments);
|
||||
router.put("/payments/status", academyController.updatePaymentStatus);
|
||||
|
||||
// ------ مدیریت دورهها ------
|
||||
router.get("/courses", academyController.getAllCourses);
|
||||
router.get("/courses/:id", academyController.getCourseById);
|
||||
router.put("/courses/:id", academyController.updateCourse);
|
||||
router.post("/courses", academyController.createCourse);
|
||||
router.put("/academy/courses/status", academyController.updateCourseStatus);
|
||||
router.delete("/courses/:courseId", academyController.admindeleteCourse);
|
||||
|
||||
// ------ مدیریت دورههای خریداری شده ------
|
||||
router.get("/purchased", academyController.getAllPurchasedCourses);
|
||||
|
||||
// ------ مدیریت آکادمیها ------
|
||||
router.get("/academies", academyController.getAllAcademies);
|
||||
router.get("/academies/:id", academyController.getAcademyById);
|
||||
router.put("/academies/:id", academyController.updateAcademy);
|
||||
router.post("/academies", academyController.createAcademy);
|
||||
router.put("/academies/status", academyController.updateAcademyStatus);
|
||||
router.delete("/academies/:id", academyController.deleteAcademy);
|
||||
|
||||
// ==========================================
|
||||
// 📤 خروجی روتها
|
||||
// ==========================================
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,45 +0,0 @@
|
||||
const express = require('express')
|
||||
const blockCheck = require('../../../middlewares/blockCheck')
|
||||
const { createAdvertisingValidationRules, createAdvertising, getAdvertisingTypes, getAdvertisingCategory, getSingleAdvertising, getAdvertisingFeatures, getAdvertisings, toggleLike, addComment, getComments, rateAdvertising, getUserAdvertisings, editAdvertising, getUserSingleAdvertising, getUserAdvertisingProfile, updateUserAdvertisingProfile, getAdvertisingsWeb, getSingleAdvertisingWeb, getUserAdvertisingProfileWeb } = require('../../../controllers/application/advertising/advertisingController')
|
||||
const auth = require('../../../middlewares/auth')
|
||||
const { initiateAdvertisingPayment, handleAdvertisingPaymentCallback, handleAdvertisingRepublishPaymentCallback, republishAdvertisingPayment, handleSuccessfulAdvertisingPayment, handleFailedAdvertisingPayment, handleFailedAdvertisingRepublishPayment, handleSuccessfulAdvertisingRepublishPayment } = require('../../../controllers/application/payment/paymentController')
|
||||
const { initiateAdvertisingPaymentWeb, handleAdvertisingPaymentCallbackWeb, republishAdvertisingPaymentWeb, handleAdvertisingRepublishPaymentCallbackWeb } = require('../../../controllers/application/payment/paymentControllerWeb')
|
||||
const router = express.Router()
|
||||
router.get('/', [auth], getAdvertisings)
|
||||
router.get('/web', getAdvertisingsWeb)
|
||||
router.get('/profile', [auth], getUserAdvertisingProfile)
|
||||
router.get('/profile-web', getUserAdvertisingProfileWeb)
|
||||
router.patch('/profile', [auth], [blockCheck], updateUserAdvertisingProfile)
|
||||
router.get('/user-advertising', [auth], getUserAdvertisings)
|
||||
router.post('/create', [auth], [blockCheck], createAdvertisingValidationRules(), createAdvertising)
|
||||
router.get('/types', [auth], getAdvertisingTypes)
|
||||
router.get('/categories', getAdvertisingCategory)
|
||||
router.get('/features', [auth], getAdvertisingFeatures)
|
||||
router.get('/get/:advertisingId', [auth], getSingleAdvertising)
|
||||
router.get('/get/web/:advertisingId', getSingleAdvertisingWeb)
|
||||
router.get('/get-detail/:advertisingId', [auth], getUserSingleAdvertising)
|
||||
router.post('/initiate-payment', [auth], [blockCheck], initiateAdvertisingPayment)
|
||||
router.post('/initiate-payment-web', [auth], [blockCheck], initiateAdvertisingPaymentWeb)
|
||||
router.post('/republish-payment', [auth], [blockCheck], republishAdvertisingPayment)
|
||||
router.post('/republish-payment-web', [auth], [blockCheck], republishAdvertisingPaymentWeb)
|
||||
router.get('/payment', handleAdvertisingPaymentCallback)
|
||||
router.get('/payment-web', handleAdvertisingPaymentCallbackWeb)
|
||||
router.get('/republish-verify-payment', handleAdvertisingRepublishPaymentCallback)
|
||||
router.get('/republish-verify-payment-web', handleAdvertisingRepublishPaymentCallbackWeb)
|
||||
router.post('/like', [blockCheck], [auth], toggleLike)
|
||||
router.post('/comment', [blockCheck], [auth], addComment)
|
||||
router.get('/comments', getComments)
|
||||
router.post('/rate', [blockCheck], [auth], rateAdvertising)
|
||||
router.post('/edit/:id', [blockCheck], [auth], editAdvertising)
|
||||
|
||||
// Route for handling successful payment
|
||||
router.post('/payment-success', handleSuccessfulAdvertisingPayment)
|
||||
// Route for handling failed payment
|
||||
router.post('/payment-failed', handleFailedAdvertisingPayment)
|
||||
// Route for handling successful payment
|
||||
router.post('/republish-payment-success', handleSuccessfulAdvertisingRepublishPayment)
|
||||
// Route for handling failed payment
|
||||
router.post('/republish-payment-failed', handleFailedAdvertisingRepublishPayment)
|
||||
|
||||
module.exports = router
|
||||
module.exports = router
|
||||
@@ -1,7 +0,0 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const citysController = require('../../../controllers/application/citysController')
|
||||
|
||||
router.get('/:id', citysController.getCities)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,6 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getExpertise } = require('../../../controllers/application/expertise/expertiseController')
|
||||
const router = express.Router()
|
||||
router.get('/', getExpertise)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,6 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getFinancial } = require('../../../controllers/application/financial/financialController')
|
||||
const router = express.Router()
|
||||
router.get('/', getFinancial)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,14 +0,0 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const { loginValidationRules, loginUser } = require('../../../controllers/application/login/loginController')
|
||||
const { verifyUser } = require('../../../controllers/application/login/verifyController')
|
||||
const { changePasswordUser, changePasswordValidationRules, editChangePasswordUser } = require('../../../controllers/application/login/changePasswordController')
|
||||
const { loginWithUserName } = require('../../../controllers/application/login/loginWithUserName')
|
||||
|
||||
router.post('/', loginValidationRules(), loginUser)
|
||||
router.post('/verify', verifyUser)
|
||||
router.post('/change_password', changePasswordValidationRules(), changePasswordUser)
|
||||
router.patch('/change_password', changePasswordValidationRules(), editChangePasswordUser)
|
||||
router.post('/login_username', loginWithUserName)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,6 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getMessages, getUnreadMessages } = require('../../../controllers/application/messages/messageController')
|
||||
const router = express.Router()
|
||||
router.get('/', getMessages)
|
||||
router.get('/unread', getUnreadMessages)
|
||||
module.exports = router
|
||||
@@ -1,6 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getNotifications, unreadNotifications } = require('../../../controllers/application/notification/notificationController')
|
||||
const router = express.Router()
|
||||
router.get('/', getNotifications)
|
||||
router.get('/unread', unreadNotifications)
|
||||
module.exports = router
|
||||
@@ -1,18 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getUserOffers, getOfferTypes, handleSuccessfulOfferPayment, handleFailedOfferPayment, updateOfferStatus, createOfferComment } = require('../../../controllers/application/offer/offerController')
|
||||
const { initiateOfferPaymentWeb, handleOfferPaymentCallback } = require('../../../controllers/application/payment/paymentControllerWeb')
|
||||
const blockCheck = require('../../../middlewares/blockCheck')
|
||||
const isRegister = require('../../../middlewares/isRegister')
|
||||
const auth = require('./../../../middlewares/auth')
|
||||
|
||||
const router = express.Router()
|
||||
router.get('/', getUserOffers)
|
||||
router.get('/types', [auth], getOfferTypes)
|
||||
router.post('/update-status', [auth], updateOfferStatus)
|
||||
router.post('/payment-success', [auth], handleSuccessfulOfferPayment)
|
||||
router.post('/payment-failed', [auth], handleFailedOfferPayment)
|
||||
router.post('/comment', [auth], createOfferComment)
|
||||
router.post('/initiate-payment-web', [auth], [blockCheck], initiateOfferPaymentWeb)
|
||||
router.get('/payment-web', handleOfferPaymentCallback)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,205 +0,0 @@
|
||||
const express = require('express');
|
||||
const { createPostValidationRules, createPost, getUserPosts, getUserPostsWeb, getPostByIdWeb } = require('../../../controllers/application/posts/postController');
|
||||
const { toggleLike } = require('../../../controllers/application/posts/likeController');
|
||||
const blockCheck = require('../../../middlewares/blockCheck');
|
||||
const auth = require('../../../middlewares/auth');
|
||||
const isHalfRegister = require('../../../middlewares/isHalfRegister');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const multer = require('multer');
|
||||
|
||||
console.log('DEBUG: Starting to load routes/posts.js');
|
||||
|
||||
const ensureDir = (dir) => {
|
||||
console.log('DEBUG: Ensuring directory exists:', dir);
|
||||
if (!fs.existsSync(dir)) {
|
||||
console.log('DEBUG: Directory does not exist, creating:', dir);
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
} else {
|
||||
console.log('DEBUG: Directory already exists:', dir);
|
||||
}
|
||||
};
|
||||
|
||||
const imageDir = path.join(__dirname, '../../../storage/posts/images');
|
||||
const videoDir = path.join(__dirname, '../../../storage/posts/videos');
|
||||
console.log('DEBUG: imageDir path:', imageDir);
|
||||
console.log('DEBUG: videoDir path:', videoDir);
|
||||
ensureDir(imageDir);
|
||||
ensureDir(videoDir);
|
||||
|
||||
console.log('DEBUG: Setting up multer storage');
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
const dir = file.mimetype.startsWith('video/') ? videoDir : imageDir;
|
||||
console.log('DEBUG: multer destination for file', file.originalname, ':', dir);
|
||||
cb(null, dir);
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
const filename = `${Date.now()}-${file.originalname}`;
|
||||
console.log('DEBUG: Generated filename for', file.originalname, ':', filename);
|
||||
cb(null, filename);
|
||||
},
|
||||
});
|
||||
|
||||
console.log('DEBUG: Setting up multer instance with limits and filter');
|
||||
|
||||
const upload = multer({
|
||||
storage,
|
||||
limits: {
|
||||
fileSize: 100 * 1024 * 1024, // حداکثر 100MB
|
||||
files: 10, // حداکثر 10 فایل
|
||||
fieldSize: 1024 * 1024,
|
||||
fieldNameSize: 100,
|
||||
},
|
||||
fileFilter: (req, file, cb) => {
|
||||
console.log('DEBUG: Checking file filter for', file.originalname, 'mimetype:', file.mimetype);
|
||||
if (file.mimetype.startsWith('image/') || file.mimetype.startsWith('video/')) {
|
||||
console.log('DEBUG: File accepted:', file.originalname);
|
||||
cb(null, true);
|
||||
} else {
|
||||
console.log('DEBUG: File rejected:', file.originalname);
|
||||
cb(new Error('فقط تصاویر یا ویدئوها مجاز هستند'), false);
|
||||
}
|
||||
},
|
||||
}).array('post_files', 10);
|
||||
|
||||
const multerErrorHandler = (err, req, res, next) => {
|
||||
console.log('DEBUG: Request headers:', JSON.stringify(req.headers, null, 2));
|
||||
console.log('DEBUG: Request body (raw):', req.body);
|
||||
console.log('DEBUG: Request files:', req.files || 'No files received');
|
||||
if (err instanceof multer.MulterError) {
|
||||
console.error('DEBUG: Multer error:', err.message, err.code, err.field);
|
||||
return res.status(400).json({
|
||||
error: true,
|
||||
message: `خطای آپلود: ${err.message} (${err.code})`,
|
||||
field: err.field,
|
||||
});
|
||||
} else if (err) {
|
||||
console.error('DEBUG: File filter error:', err.message);
|
||||
return res.status(400).json({
|
||||
error: true,
|
||||
message: err.message,
|
||||
});
|
||||
}
|
||||
console.log('DEBUG: Multer processing completed, files:', req.files || 'No files');
|
||||
console.log('DEBUG: Request body after multer:', req.body);
|
||||
next();
|
||||
};
|
||||
|
||||
console.log('DEBUG: Setting up express router');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// روت اصلی با multipart/form-data
|
||||
router.post(
|
||||
'/create',
|
||||
(req, res, next) => {
|
||||
console.log('DEBUG: reached auth');
|
||||
console.log('DEBUG: Raw request headers:', JSON.stringify(req.headers, null, 2));
|
||||
next();
|
||||
},
|
||||
auth,
|
||||
(req, res, next) => {
|
||||
console.log('DEBUG: reached blockCheck');
|
||||
next();
|
||||
},
|
||||
blockCheck,
|
||||
(req, res, next) => {
|
||||
console.log('DEBUG: reached isHalfRegister');
|
||||
next();
|
||||
},
|
||||
isHalfRegister,
|
||||
(req, res, next) => {
|
||||
console.log('DEBUG: reached multer');
|
||||
console.log('DEBUG: Content-Length:', req.headers['content-length']);
|
||||
console.log('DEBUG: Content-Type:', req.headers['content-type']);
|
||||
next();
|
||||
},
|
||||
upload,
|
||||
multerErrorHandler,
|
||||
(req, res, next) => {
|
||||
console.log('DEBUG: reached validation');
|
||||
console.log('DEBUG: Parsed files:', req.files || 'No files');
|
||||
console.log('DEBUG: Parsed body:', req.body);
|
||||
next();
|
||||
},
|
||||
createPostValidationRules(),
|
||||
(req, res, next) => {
|
||||
console.log('DEBUG: reached controller');
|
||||
next();
|
||||
},
|
||||
createPost
|
||||
);
|
||||
|
||||
// روت جدید برای Base64
|
||||
router.post(
|
||||
'/create-base64',
|
||||
(req, res, next) => {
|
||||
console.log('DEBUG: reached auth for base64');
|
||||
console.log('DEBUG: Raw request headers:', JSON.stringify(req.headers, null, 2));
|
||||
next();
|
||||
},
|
||||
auth,
|
||||
(req, res, next) => {
|
||||
console.log('DEBUG: reached blockCheck for base64');
|
||||
next();
|
||||
},
|
||||
blockCheck,
|
||||
(req, res, next) => {
|
||||
console.log('DEBUG: reached isHalfRegister for base64');
|
||||
next();
|
||||
},
|
||||
isHalfRegister,
|
||||
(req, res, next) => {
|
||||
console.log('DEBUG: Request body (base64):', JSON.stringify(req.body, null, 2));
|
||||
next();
|
||||
},
|
||||
createPostValidationRules(),
|
||||
async (req, res) => {
|
||||
try {
|
||||
const { files, caption } = req.body;
|
||||
console.log('DEBUG: Received files (base64):', files.length);
|
||||
console.log('DEBUG: Received caption:', caption);
|
||||
|
||||
// تبدیل Base64 به فایل
|
||||
const savedFiles = await Promise.all(
|
||||
files.map(async (file, index) => {
|
||||
const buffer = Buffer.from(file.data.split(',')[1], 'base64');
|
||||
const dir = file.type.startsWith('video/') ? videoDir : imageDir;
|
||||
const filename = `${Date.now()}-${index}-${file.name}`;
|
||||
const filePath = path.join(dir, filename);
|
||||
console.log('DEBUG: Saving file:', filePath);
|
||||
await fs.promises.writeFile(filePath, buffer);
|
||||
return {
|
||||
path: filePath,
|
||||
filename,
|
||||
mimetype: file.type,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// فراخوانی createPost با فرمت مشابه
|
||||
req.files = savedFiles;
|
||||
req.body = { caption };
|
||||
console.log('DEBUG: Prepared files for createPost:', savedFiles);
|
||||
console.log('DEBUG: Prepared body for createPost:', req.body);
|
||||
await createPost(req, res);
|
||||
} catch (err) {
|
||||
console.error('DEBUG: Base64 processing error:', err.message);
|
||||
res.status(400).json({
|
||||
error: true,
|
||||
message: `خطای پردازش فایلها: ${err.message}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
router.post('/like', [auth, blockCheck], toggleLike);
|
||||
router.get('/web/:postId', getPostByIdWeb);
|
||||
router.get('/user-posts', [auth], getUserPosts);
|
||||
router.get('/user-posts/web', getUserPostsWeb);
|
||||
|
||||
console.log('DEBUG: Finished loading routes/posts.js');
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,8 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getProfile , getUserById} = require('../../../controllers/application/profile/profileController')
|
||||
const auth = require('../../../middlewares/auth')
|
||||
const router = express.Router()
|
||||
router.get('/',[auth], getProfile)
|
||||
router.get('/:id', getUserById)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,44 +0,0 @@
|
||||
const express = require('express')
|
||||
const { createProject, createProjectValidationRules } = require('../../../controllers/application/projects/createProjectController')
|
||||
const { getProjects, getSingleProjects, getSingleProjectsWeb, getUserProjects, getUserProjectsWeb } = require('../../../controllers/application/projects/getProjectController')
|
||||
const { getProjectTypes } = require('../../../controllers/application/projects/projectTypesController')
|
||||
const { initiatePayment, handlePaymentCallback, paymentRequestStepOne, paymentRequestStepTwo, paymentRequestStepAll, paymentRequestStepOneCallback, paymentRequestStepTwoCallback, paymentRequestStepAllCallback, handleSuccessfulPayment, handleFailedPayment } = require('../../../controllers/application/payment/paymentController')
|
||||
const auth = require('../../../middlewares/auth')
|
||||
const { requestProjectValidationRules, requestProject, acceptProject, editRequestProject } = require('../../../controllers/application/projects/requestProjectController')
|
||||
const { editProject, doneProject, cancleProject, editProjectValidationRules, setRateProject, doneProjectWeb } = require('../../../controllers/application/projects/updateProjectController')
|
||||
const blockCheck = require('../../../middlewares/blockCheck')
|
||||
const isRegister = require('../../../middlewares/isRegister')
|
||||
const { initiatePaymentWeb, handlePaymentCallbackWeb, paymentRequestAcceptWeb } = require('../../../controllers/application/payment/paymentControllerWeb')
|
||||
const router = express.Router()
|
||||
router.get('/', getProjects)
|
||||
router.get('/user-projects', [auth], getUserProjects)
|
||||
router.get('/user-projects/web', getUserProjectsWeb)
|
||||
router.get('/types', [auth], getProjectTypes)
|
||||
router.get('/get/:projectId', [auth], getSingleProjects)
|
||||
router.get('/get/web/:projectId', getSingleProjectsWeb)
|
||||
router.get('/payment', handlePaymentCallback)
|
||||
router.get('/payment-web', handlePaymentCallbackWeb)
|
||||
router.post('/create', [blockCheck], [auth], createProjectValidationRules(), createProject)
|
||||
router.post('/edit', [blockCheck], [auth], editProjectValidationRules(), editProject)
|
||||
router.post('/rate', [blockCheck], [auth], [isRegister], setRateProject)
|
||||
router.post('/initiate-payment', [blockCheck], [isRegister], [auth], initiatePayment)
|
||||
router.post('/initiate-payment-web', [blockCheck], [isRegister], [auth], initiatePaymentWeb)
|
||||
router.post('/request', [blockCheck], [isRegister], [auth], requestProjectValidationRules(), requestProject)
|
||||
router.post('/edit-request', [isRegister], [blockCheck], [auth], requestProjectValidationRules(), editRequestProject)
|
||||
router.post('/payment-request-step-one', [isRegister], [blockCheck], [auth], paymentRequestStepOne)
|
||||
router.post('/payment-request-step-one/web', [isRegister], [blockCheck], [auth], paymentRequestAcceptWeb)
|
||||
router.post('/payment-request-step-two', [isRegister], [blockCheck], [auth], paymentRequestStepTwo)
|
||||
router.post('/payment-request-all', [isRegister], [blockCheck], [auth], paymentRequestStepAll)
|
||||
router.get('/payment-request-step-one-callback', paymentRequestStepOneCallback)
|
||||
router.get('/payment-request-step-two-callback', paymentRequestStepTwoCallback)
|
||||
router.get('/payment-request-step-all-callback', paymentRequestStepAllCallback)
|
||||
router.post('/accept', [isRegister], [blockCheck], [auth], acceptProject)
|
||||
router.post('/done', [isRegister], [auth], doneProject)
|
||||
router.post('/done/web', [isRegister], [auth], doneProjectWeb)
|
||||
router.post('/cancle', [isRegister], [blockCheck], [auth], cancleProject)
|
||||
|
||||
// Route for handling successful payment
|
||||
router.post('/payment-success', handleSuccessfulPayment)
|
||||
// Route for handling failed payment
|
||||
router.post('/payment-failed', handleFailedPayment)
|
||||
module.exports = router
|
||||
@@ -1,7 +0,0 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const provincesController = require('../../../controllers/application/provincesController')
|
||||
|
||||
router.get('/', provincesController.getProvinces)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,16 +0,0 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const { registerValidationRules, registerUser } = require('../../../controllers/application/register/registerController')
|
||||
const { verifyUser } = require('../../../controllers/application/register/verifyController')
|
||||
const { setUserName, updateUserName } = require('../../../controllers/application/register/usernameController')
|
||||
const { setUserFullName, setUserFullNameValidationRules } = require('../../../controllers/application/register/userFullNameController')
|
||||
const { setUserType } = require('../../../controllers/application/register/userTypeController')
|
||||
|
||||
router.post('/', registerValidationRules(), registerUser)
|
||||
router.post('/verify', verifyUser)
|
||||
router.post('/username', setUserName)
|
||||
router.patch('/username', updateUserName)
|
||||
router.post('/fullname', setUserFullNameValidationRules(), setUserFullName)
|
||||
router.post('/user_type', setUserType)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,6 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getSearch } = require('../../../controllers/application/search-web/searchController')
|
||||
const router = express.Router()
|
||||
router.get('/', getSearch)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,6 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getSearch } = require('../../../controllers/application/search/searchController')
|
||||
const router = express.Router()
|
||||
router.get('/', getSearch)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,6 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getSetting } = require('../../../controllers/application/settings/settingsController')
|
||||
const router = express.Router()
|
||||
router.get('/:key', getSetting)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,19 +0,0 @@
|
||||
const express = require('express')
|
||||
const auth = require('../../../middlewares/auth')
|
||||
const blockCheck = require('../../../middlewares/blockCheck')
|
||||
const isHalfRegister = require('../../../middlewares/isHalfRegister')
|
||||
const {
|
||||
createStoryBase64,
|
||||
getStoriesFeed,
|
||||
markStoryViewed,
|
||||
deleteStory
|
||||
} = require('../../../controllers/application/stories/storyController')
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.get('/feed', getStoriesFeed)
|
||||
router.post('/create-base64', auth, blockCheck, isHalfRegister, createStoryBase64)
|
||||
router.post('/view', auth, blockCheck, markStoryViewed)
|
||||
router.delete('/:storyId', auth, blockCheck, deleteStory)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,9 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getUserTickets, createTicket, addUserMessageToTicket, getTicketMessages } = require('../../../controllers/application/tickets/ticketsController')
|
||||
const router = express.Router()
|
||||
router.get('/', getUserTickets)
|
||||
router.get('/messages', getTicketMessages)
|
||||
router.post('/create', createTicket)
|
||||
router.post('/message', addUserMessageToTicket)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,23 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getUsers, getSingleUser, blockUser, unblockUser, getComments,createLicense, getAllLicenses,updateLicenseConfirmation, getLicenseByUserId, inviteEmployer, getUserContactInfo, getPostsWeb, getSingleUserWeb, createUserComment, recordExploreInteraction } = require('../../../controllers/application/users/getUserController')
|
||||
const blockCheck = require('../../../middlewares/blockCheck')
|
||||
const isRegister = require('../../../middlewares/isRegister')
|
||||
const auth = require('../../../middlewares/auth')
|
||||
const router = express.Router()
|
||||
router.get('/', [auth], getUsers)
|
||||
router.get('/web', getPostsWeb)
|
||||
router.get('/get', [auth], getSingleUser)
|
||||
router.get('/get/web', getSingleUserWeb)
|
||||
router.post('/block', [auth], [isRegister], [blockCheck], blockUser)
|
||||
router.post('/unblock', [auth], [isRegister], [blockCheck], unblockUser)
|
||||
router.get('/comments', [auth], getComments)
|
||||
router.post('/comments', [auth], [isRegister], [blockCheck], createUserComment)
|
||||
router.post('/explore/interaction', [auth], recordExploreInteraction)
|
||||
router.get('/invite-employer', [auth], [isRegister], [blockCheck], inviteEmployer)
|
||||
router.get('/contact-info', [auth], getUserContactInfo)
|
||||
router.get('/License', getAllLicenses)
|
||||
router.get('/License/:userId', getLicenseByUserId)
|
||||
router.post('/License', createLicense)
|
||||
router.put('/License/:userId', updateLicenseConfirmation)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,40 +0,0 @@
|
||||
const express = require('express')
|
||||
const { setProfileImage, updateProfileImage } = require('../../../controllers/application/verify/setProfileImageController')
|
||||
const { setExpertise, updateExpertise } = require('../../../controllers/application/verify/expertiseController')
|
||||
const { setGender } = require('../../../controllers/application/verify/genderController')
|
||||
const { setSizes, updateSizes } = require('../../../controllers/application/verify/sizesController')
|
||||
const { setColors, updateColors } = require('../../../controllers/application/verify/colorsController')
|
||||
const { setCooperationType, updateCooperationType } = require('../../../controllers/application/verify/cooperationTypeController')
|
||||
const { setConversation, updateConversation } = require('../../../controllers/application/verify/conversationController')
|
||||
const { saveAuth, saveAuthValidationRules, updateShaba } = require('../../../controllers/application/verify/authController')
|
||||
const { setAddress, updateAddress } = require('../../../controllers/application/verify/addressController')
|
||||
const { setNationalCardImage } = require('../../../controllers/application/verify/setNationalCardImageController')
|
||||
const { completeRegistration } = require('../../../controllers/application/verify/completeRegistrationController')
|
||||
const blockCheck = require('../../../middlewares/blockCheck')
|
||||
const auth = require('../../../middlewares/auth')
|
||||
const { setServices, updateServices } = require('../../../controllers/application/verify/servicesController')
|
||||
const router = express.Router()
|
||||
|
||||
router.post('/profile_image', setProfileImage)
|
||||
router.patch('/profile_image', [blockCheck], [auth], updateProfileImage)
|
||||
router.post('/expertise', setExpertise)
|
||||
router.patch('/expertise', [blockCheck], [auth], updateExpertise)
|
||||
router.post('/gender', setGender)
|
||||
router.post('/sizes', setSizes)
|
||||
router.patch('/sizes', [blockCheck], [auth], updateSizes)
|
||||
router.post('/services', setServices)
|
||||
router.patch('/services', [blockCheck], [auth], updateServices)
|
||||
router.post('/colors', setColors)
|
||||
router.patch('/colors', [blockCheck], [auth], updateColors)
|
||||
router.post('/cooperation_type', setCooperationType)
|
||||
router.patch('/cooperation_type', [blockCheck], [auth], updateCooperationType)
|
||||
router.post('/conversation', setConversation)
|
||||
router.patch('/conversation', [blockCheck], [auth], updateConversation)
|
||||
router.post('/auth', saveAuthValidationRules(), saveAuth)
|
||||
router.patch('/shaba', [blockCheck], [auth], updateShaba)
|
||||
router.post('/address', setAddress)
|
||||
router.patch('/address', [blockCheck], [auth], updateAddress)
|
||||
router.post('/national_card_image', setNationalCardImage)
|
||||
router.post('/complete', completeRegistration)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,6 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getVersion } = require('../../../controllers/application/version/versionController')
|
||||
const router = express.Router()
|
||||
router.get('/', getVersion)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,8 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getProjects, getSingleProject, getProjectRequests } = require('../../../controllers/application/workroom/workroomController')
|
||||
const router = express.Router()
|
||||
router.get('/', getProjects)
|
||||
router.get('/get', getSingleProject)
|
||||
router.get('/get-requests/:projectId', getProjectRequests)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,79 +0,0 @@
|
||||
const registerRouter = require('./application/register')
|
||||
const loginRouter = require('./application/login')
|
||||
const verifyRouter = require('./application/verify')
|
||||
const provincesRouter = require('./application/provinces')
|
||||
const citiesRouter = require('./application/city')
|
||||
const projectsRouter = require('./application/projects')
|
||||
const usersRouter = require('./application/users')
|
||||
const postsRouter = require('./application/posts')
|
||||
const expertiseRouter = require('./application/expertise')
|
||||
const profileRouter = require('./application/profile')
|
||||
const workroomRouter = require('./application/workroom')
|
||||
const messagesRouter = require('./application/messages')
|
||||
const searchRouter = require('./application/search')
|
||||
const searchRouterWeb = require('./application/search-web')
|
||||
const financialRouter = require('./application/financial')
|
||||
const notificationRouter = require('./application/notification')
|
||||
const ticketsRouter = require('./application/tickets')
|
||||
const advertisingRouter = require('./application/advertising')
|
||||
const settingsRouter = require('./application/settings')
|
||||
const versionRouter = require('./application/version')
|
||||
const offerRouter = require('./application/offers')
|
||||
const academyRoute = require('./application/academy')
|
||||
const storiesRouter = require('./application/stories')
|
||||
|
||||
const projectsPanelRouter = require('./panel/projects')
|
||||
const postsPanelRouter = require('./panel/posts')
|
||||
const expertisePanelRouter = require('./panel/expertise')
|
||||
const loginPanelRouter = require('./panel/login')
|
||||
const usersPanelRouter = require('./panel/users')
|
||||
const ticketsPanelRouter = require('./panel/tickets')
|
||||
const financialPanelRouter = require('./panel/financial')
|
||||
const commentsPanelRouter = require('./panel/comments')
|
||||
const advertisingPanelRouter = require('./panel/advertising')
|
||||
const shopsPanelRouter = require('./panel/shops')
|
||||
const settingsPanelRouter = require('./panel/settings')
|
||||
const versionPanelRouter = require('./panel/version')
|
||||
|
||||
const auth = require('../middlewares/auth')
|
||||
const adminAuth = require('../middlewares/adminAuth')
|
||||
module.exports = (app) => {
|
||||
// App
|
||||
app.use('/api/v1/register', registerRouter)
|
||||
app.use('/api/v1/login', loginRouter)
|
||||
app.use('/api/v1/verify', [auth], verifyRouter)
|
||||
app.use('/api/v1/provinces', provincesRouter)
|
||||
app.use('/api/v1/cities', citiesRouter)
|
||||
app.use('/api/v1/projects', projectsRouter)
|
||||
app.use('/api/v1/users', usersRouter)
|
||||
app.use('/api/v1/posts', postsRouter)
|
||||
app.use('/api/v1/expertise', expertiseRouter)
|
||||
app.use('/api/v1/profile', profileRouter)
|
||||
app.use('/api/v1/workroom', [auth], workroomRouter)
|
||||
app.use('/api/v1/messages', [auth], messagesRouter)
|
||||
app.use('/api/v1/notifications', [auth], notificationRouter)
|
||||
app.use('/api/v1/search', [auth], searchRouter)
|
||||
app.use('/api/v1/search-web', searchRouterWeb)
|
||||
app.use('/api/v1/financial', [auth], financialRouter)
|
||||
app.use('/api/v1/tickets', [auth], ticketsRouter)
|
||||
app.use('/api/v1/offers', offerRouter)
|
||||
app.use('/api/v1/advertising', advertisingRouter)
|
||||
app.use('/api/v1/settings', settingsRouter)
|
||||
app.use('/api/v1/version', versionRouter)
|
||||
app.use('/api/v1/academy', academyRoute)
|
||||
app.use('/api/v1/stories', storiesRouter)
|
||||
|
||||
// Panel
|
||||
app.use('/api/v1/panel/login', loginPanelRouter)
|
||||
app.use('/api/v1/panel/users', usersPanelRouter)
|
||||
app.use('/api/v1/panel/projects', [adminAuth], projectsPanelRouter)
|
||||
app.use('/api/v1/panel/posts', [adminAuth], postsPanelRouter)
|
||||
app.use('/api/v1/panel/expertise', [adminAuth], expertisePanelRouter)
|
||||
app.use('/api/v1/panel/tickets', [adminAuth], ticketsPanelRouter)
|
||||
app.use('/api/v1/panel/financial', [adminAuth], financialPanelRouter)
|
||||
app.use('/api/v1/panel/comments', [adminAuth], commentsPanelRouter)
|
||||
app.use('/api/v1/panel/advertising', [adminAuth], advertisingPanelRouter)
|
||||
app.use('/api/v1/panel/shops', [adminAuth], shopsPanelRouter)
|
||||
app.use('/api/v1/panel/settings', [adminAuth], settingsPanelRouter)
|
||||
app.use('/api/v1/panel/version', [adminAuth], versionPanelRouter)
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getAdvertisings, getSingleAdvertising, acceptAdvertising, rejectAdvertising, getAdvertisingCategories, getAdvertisingCategoryById,createAdvertisingCategory , deleteAdvertisingCategory } = require('../../../controllers/panel/advertising/advertisingController')
|
||||
const { getAdvertisingComments, getAdvertisingCommentDetail, acceptAdvertisingComment } = require('../../../controllers/panel/comments/advertisingCommentController')
|
||||
const router = express.Router()
|
||||
router.get('/', getAdvertisings)
|
||||
router.get('/detail', getSingleAdvertising)
|
||||
router.post('/accept', acceptAdvertising)
|
||||
router.post('/reject', rejectAdvertising)
|
||||
|
||||
router.get('/comments', getAdvertisingComments)
|
||||
router.get('/comments/detail', getAdvertisingCommentDetail)
|
||||
router.post('/comments/accept', acceptAdvertisingComment)
|
||||
|
||||
router.post('/advertising-categories', createAdvertisingCategory)
|
||||
router.delete('/advertising-categories/:id', deleteAdvertisingCategory)
|
||||
// لیست دستهبندیها (پشتیبانی از query params)
|
||||
router.get('/advertising-categories', getAdvertisingCategories);
|
||||
|
||||
// گرفتن یک دستهبندی با id
|
||||
router.get('/advertising-categories/:id', getAdvertisingCategoryById);
|
||||
|
||||
module.exports = router
|
||||
@@ -1,8 +0,0 @@
|
||||
const express = require('express')
|
||||
const { acceptComment, getComments, getCommentDetail } = require('../../../controllers/panel/comments/commentController')
|
||||
const router = express.Router()
|
||||
router.get('/', getComments)
|
||||
router.get('/detail', getCommentDetail)
|
||||
router.post('/accept', acceptComment)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,9 +0,0 @@
|
||||
const express = require('express')
|
||||
const { createExpertise, createSubExpertise, getExpertise, editExpertise } = require('../../../controllers/panel/expertise/expertiseController')
|
||||
const router = express.Router()
|
||||
router.get('/', getExpertise)
|
||||
router.post('/create', createExpertise)
|
||||
router.post('/create/sub', createSubExpertise)
|
||||
router.put('/update/:id', editExpertise)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,7 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getFinancial, getFinancialDetail } = require('../../../controllers/panel/financial/financialController')
|
||||
const router = express.Router()
|
||||
router.get('/', getFinancial)
|
||||
router.get('/detail', getFinancialDetail)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,8 +0,0 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const { adminLogin, changePassword } = require('../../../controllers/panel/login/loginController')
|
||||
const adminAuth = require('../../../middlewares/adminAuth')
|
||||
router.post('/', adminLogin)
|
||||
router.post('/change-password', [adminAuth], changePassword)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,8 +0,0 @@
|
||||
const express = require('express')
|
||||
const { acceptPost, getPosts, getPostDetail } = require('../../../controllers/panel/posts/postController')
|
||||
const router = express.Router()
|
||||
router.get('/', getPosts)
|
||||
router.get('/detail', getPostDetail)
|
||||
router.post('/accept', acceptPost)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,11 +0,0 @@
|
||||
const express = require('express')
|
||||
const { createProjectTypes, getProjectTypes } = require('../../../controllers/panel/projects/projectTypesController')
|
||||
const { getProjects, getProjectDetails, acceptProject, rejectProject } = require('../../../controllers/panel/projects/projectController')
|
||||
const router = express.Router()
|
||||
router.get('/', getProjects)
|
||||
router.get('/detail', getProjectDetails)
|
||||
router.post('/accept', acceptProject)
|
||||
router.post('/reject', rejectProject)
|
||||
router.get('/types', getProjectTypes)
|
||||
router.post('/types/create', createProjectTypes)
|
||||
module.exports = router
|
||||
@@ -1,7 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getSetting, updateSetting } = require('../../../controllers/panel/settings/settingsController')
|
||||
const router = express.Router()
|
||||
router.get('/:key', getSetting)
|
||||
router.put('/:key', updateSetting)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,8 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getShops, getShopDetail, updateShop } = require('../../../controllers/panel/shops/shopsController')
|
||||
const router = express.Router()
|
||||
router.get('/', getShops)
|
||||
router.get('/detail', getShopDetail)
|
||||
router.put('/update', updateShop)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,9 +0,0 @@
|
||||
const express = require('express')
|
||||
const { addAdminMessageToTicket, getAllTickets, getTicketMessages, closeTicket } = require('../../../controllers/panel/tickets/ticketsController')
|
||||
const router = express.Router()
|
||||
router.get('/', getAllTickets)
|
||||
router.get('/messages', getTicketMessages)
|
||||
router.post('/message', addAdminMessageToTicket)
|
||||
router.post('/close', closeTicket)
|
||||
|
||||
module.exports = router
|
||||
@@ -1,12 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getUsers, getUserDetail,changePass, updateUser, verifyUser, rejectUser, updateUserStatus } = require('../../../controllers/panel/users/usersController')
|
||||
const router = express.Router()
|
||||
router.get('/', getUsers)
|
||||
router.post('/change-password', changePass)
|
||||
router.get('/detail', getUserDetail)
|
||||
router.put('/update', updateUser)
|
||||
router.post('/verify', verifyUser)
|
||||
router.post('/reject', rejectUser)
|
||||
router.post('/update-status', updateUserStatus);
|
||||
|
||||
module.exports = router
|
||||
@@ -1,7 +0,0 @@
|
||||
const express = require('express')
|
||||
const { getVersion, upsertVersion } = require('../../../controllers/panel/version/versionController')
|
||||
const router = express.Router()
|
||||
router.get('/', getVersion)
|
||||
router.post('/', upsertVersion)
|
||||
|
||||
module.exports = router
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,16 @@
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/* 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
|
||||
}
|
||||
/* 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
|
||||
}
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
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 }
|
||||
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 }
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
/* 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
|
||||
}
|
||||
/* 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
|
||||
}
|
||||
|
||||
@@ -1,92 +1,92 @@
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const { check, validationResult } = require('express-validator')
|
||||
const { default: axios } = require('axios')
|
||||
const { OTP_VALID_MS } = require('../../../utils/otpExpiry')
|
||||
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()
|
||||
const otpSentAt = new Date()
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const user = await UserModel.findOneAndUpdate(
|
||||
{ mobile },
|
||||
{ $set: { mobile, otp: String(otp), otpSentAt } },
|
||||
{ upsert: true, new: true, lean: true }
|
||||
)
|
||||
setTimeout(() => {
|
||||
UserModel.findOneAndUpdate(
|
||||
{ mobile },
|
||||
{ $set: { otp: null, otpSentAt: null } },
|
||||
{ new: true }
|
||||
)
|
||||
.then(() => {})
|
||||
.catch(error => console.error('Error setting OTP to null:', error))
|
||||
}, OTP_VALID_MS)
|
||||
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
|
||||
}
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const { check, validationResult } = require('express-validator')
|
||||
const { default: axios } = require('axios')
|
||||
const { OTP_VALID_MS } = require('../../../utils/otpExpiry')
|
||||
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()
|
||||
const otpSentAt = new Date()
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const user = await UserModel.findOneAndUpdate(
|
||||
{ mobile },
|
||||
{ $set: { mobile, otp: String(otp), otpSentAt } },
|
||||
{ upsert: true, new: true, lean: true }
|
||||
)
|
||||
setTimeout(() => {
|
||||
UserModel.findOneAndUpdate(
|
||||
{ mobile },
|
||||
{ $set: { otp: null, otpSentAt: null } },
|
||||
{ new: true }
|
||||
)
|
||||
.then(() => {})
|
||||
.catch(error => console.error('Error setting OTP to null:', error))
|
||||
}, OTP_VALID_MS)
|
||||
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
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user