Add full project files

This commit is contained in:
root
2026-07-04 19:24:56 +03:30
parent d54f5441a3
commit b245e7b71a
835 changed files with 30149 additions and 0 deletions

View File

@@ -0,0 +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 });
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +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),
replyTo,
forwardedFrom: doc.forwardedFrom || undefined
}
}
function emitChatEvents(io, message, senderId, receiverId) {
const payload = message
const roomA = `chat:${senderId}:${receiverId}`
const roomB = `chat:${receiverId}:${senderId}`
io.to(roomA).to(roomB).emit('newMessage', payload)
io.to(`user:${senderId}`).to(`user:${receiverId}`).emit('chatListUpdate', {
senderId,
receiverId,
message: payload
})
}
function parseForwardedContent(content) {
if (!content) return { body: '', forwardedFrom: null }
try {
const line = content.split('\n')[0]
const j = JSON.parse(line)
if (j.forwardedFrom) {
return {
forwardedFrom: j.forwardedFrom,
body: content.replace(/^\{.*\}\n?/, '').trim()
}
}
} catch (_) {
/* not json */
}
return { body: content, forwardedFrom: null }
}
module.exports = {
formatTimeOnly,
enrichMessage,
emitChatEvents,
parseForwardedContent
}

View File

@@ -0,0 +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}`)
})
}

View File

@@ -0,0 +1,18 @@
[Wed Mar 13 00:31:27 2024] [Token] {"TokenKey":"OVlma3FmZmJOTjNoY1A3bmxRbE5odHBHTTNZbGpjcGxvcVVJTmZIMDFHdjkvWDNYaG55WlB0cXNpaWRmclVCeHlXNFVrd2xxZThZQktIVEdGak9Xd29QTGNhMWtpYXlWUmwwNXcvZUdxQjhmbHFyenpPNVlDVk9ZbGZsUmplQ3NjUHdHNE5ma0hGdHpQV040MkV0OUx0M285SkdBcGp6OFA5SGpoNHZHUzFEajkwc0ZWQWRWSlNBNFlOMWxxOC9k","IsSuccessful":true,"Message":" درخواست Token با موفقیت انجام شد"}
[Wed Mar 13 00:31:27 2024] [VerificationCode] {"VerificationCodeId":634458365,"IsSuccessful":true,"Message":"your verification code is sent"}
[Wed Mar 13 00:34:14 2024] [Token] {"TokenKey":"OVlma3FmZmJOTjNoY1A3bmxRbE5odHBHTTNZbGpjcGxvcVVJTmZIMDFHdjkvWDNYaG55WlB0cXNpaWRmclVCeHlXNFVrd2xxZThZQktIVEdGak9Xd29QTGNhMWtpYXlWUmwwNXcvZUdxQjhmbHFyenpPNVlDVk9ZbGZsUmplQ3M0WTRVeG9SeVNtN0xnMWUzQWo4WG1Oam9mNUw2SkJPV04rMGVOMUJ6Njhpb1RXMTl2LzFEdGRESXhZazBwaHBk","IsSuccessful":true,"Message":" درخواست Token با موفقیت انجام شد"}
[Wed Mar 13 00:34:15 2024] [VerificationCode] {"VerificationCodeId":634459857,"IsSuccessful":true,"Message":"your verification code is sent"}
[Wed Mar 13 00:34:48 2024] [Token] {"TokenKey":"OVlma3FmZmJOTjNoY1A3bmxRbE5odHBHTTNZbGpjcGxvcVVJTmZIMDFHdjkvWDNYaG55WlB0cXNpaWRmclVCeHlXNFVrd2xxZThZQktIVEdGak9Xd29QTGNhMWtpYXlWUmwwNXcvZUdxQjhmbHFyenpPNVlDVk9ZbGZsUmplQ3NGN0NVV3V1K01VRTg3TGY1by9PcmtlSVpWaFpBa0l4RXNCOERHMzIxMDRkL1RYbWRhOXc5ZmovT0lFV0FDamJs","IsSuccessful":true,"Message":" درخواست Token با موفقیت انجام شد"}
[Wed Mar 13 00:34:48 2024] [VerificationCode] {"VerificationCodeId":634460165,"IsSuccessful":true,"Message":"your verification code is sent"}
[Wed Mar 13 00:34:57 2024] [Token] {"TokenKey":"OVlma3FmZmJOTjNoY1A3bmxRbE5odHBHTTNZbGpjcGxvcVVJTmZIMDFHdjkvWDNYaG55WlB0cXNpaWRmclVCeHlXNFVrd2xxZThZQktIVEdGak9Xd29QTGNhMWtpYXlWUmwwNXcvZUdxQjhmbHFyenpPNVlDVk9ZbGZsUmplQ3NyNmxiRjUwVlF6UUlkSStnOWpOc0lLSyt5VHJjSjFRa0xsTkk0dFB2L3VSaE1PTDdQREY4WGlzcUFQeE01QWRP","IsSuccessful":true,"Message":" درخواست Token با موفقیت انجام شد"}
[Wed Mar 13 00:34:57 2024] [VerificationCode] {"VerificationCodeId":634460241,"IsSuccessful":true,"Message":"your verification code is sent"}
[Wed Mar 13 00:39:39 2024] [Token] {"TokenKey":"OVlma3FmZmJOTjNoY1A3bmxRbE5odHBHTTNZbGpjcGxvcVVJTmZIMDFHdjkvWDNYaG55WlB0cXNpaWRmclVCeHlXNFVrd2xxZThZQktIVEdGak9Xd29QTGNhMWtpYXlWUmwwNXcvZUdxQjhmbHFyenpPNVlDVk9ZbGZsUmplQ3N6MHErTmJEVzMydjZRcVJQaHNLb0R3MzJKcWk5bGJYNTh5UzFmSEpSSUY0b0Q4MFppdDRoeFlxWTZZSEQrL1hL","IsSuccessful":true,"Message":" درخواست Token با موفقیت انجام شد"}
[Wed Mar 13 00:39:39 2024] [VerificationCode] {"VerificationCodeId":634462768,"IsSuccessful":true,"Message":"your verification code is sent"}
[Wed Mar 13 00:40:18 2024] [Token] {"TokenKey":"OVlma3FmZmJOTjNoY1A3bmxRbE5odHBHTTNZbGpjcGxvcVVJTmZIMDFHdjkvWDNYaG55WlB0cXNpaWRmclVCeHlXNFVrd2xxZThZQktIVEdGak9Xd29QTGNhMWtpYXlWUmwwNXcvZUdxQjhmbHFyenpPNVlDVk9ZbGZsUmplQ3NLRlVYejdBcTJxMGl6dXJVSTRsQkNiOGh2M241WTZrSGI3Z0JCY3kxeFZvUzVCTnRkdmVWalFzd25KQzIzV21I","IsSuccessful":true,"Message":" درخواست Token با موفقیت انجام شد"}
[Wed Mar 13 00:40:18 2024] [VerificationCode] {"VerificationCodeId":634463121,"IsSuccessful":true,"Message":"your verification code is sent"}
[Wed Mar 13 00:41:38 2024] [Token] {"TokenKey":"OVlma3FmZmJOTjNoY1A3bmxRbE5odHBHTTNZbGpjcGxvcVVJTmZIMDFHdjkvWDNYaG55WlB0cXNpaWRmclVCeHlXNFVrd2xxZThZQktIVEdGak9Xd29QTGNhMWtpYXlWUmwwNXcvZUdxQjhmbHFyenpPNVlDVk9ZbGZsUmplQ3NOMUs3ZVN3eDU0OVBZdXR1VU1Yd3dhS2lyWmMrWkpjTGpUVVkzWDQ4ZU10VDBTWnFZc0gwYlVSZHArS1FBU2ZL","IsSuccessful":true,"Message":" درخواست Token با موفقیت انجام شد"}
[Wed Mar 13 00:41:38 2024] [VerificationCode] {"VerificationCodeId":634463845,"IsSuccessful":true,"Message":"your verification code is sent"}
[Wed Mar 13 01:11:21 2024] [Token] {"TokenKey":"OVlma3FmZmJOTjNoY1A3bmxRbE5odHBHTTNZbGpjcGxvcVVJTmZIMDFHdjkvWDNYaG55WlB0cXNpaWRmclVCeHlXNFVrd2xxZThZQktIVEdGak9Xd29QTGNhMWtpYXlWUmwwNXcvZUdxQjhmbHFyenpPNVlDVk9ZbGZsUmplQ3M4em1oSG05eU9XbVlna0U1TDRjRWdlRi8zTE10YnAzYVZia0hRTmQ3bWV4cEUyUUl4TCtPMncvWStrNi9TSjNt","IsSuccessful":true,"Message":" درخواست Token با موفقیت انجام شد"}
[Wed Mar 13 01:11:21 2024] [VerificationCode] {"VerificationCodeId":634470192,"IsSuccessful":true,"Message":"your verification code is sent"}
[Wed Mar 13 02:55:34 2024] [Token] {"TokenKey":"OVlma3FmZmJOTjNoY1A3bmxRbE5odHBHTTNZbGpjcGxvcVVJTmZIMDFHdjkvWDNYaG55WlB0cXNpaWRmclVCeHlXNFVrd2xxZThZQktIVEdGak9Xd29QTGNhMWtpYXlWUmwwNXcvZUdxQjhmbHFyenpPNVlDVk9ZbGZsUmplQ3MwSFZidjNtY0p1T3RwQVFlbkxJdjFHZ0xUaFpqZW9CdllCR21oNFNyaXRUczFaVUVSemZ3Rzk0TW0zRG50Q3VU","IsSuccessful":true,"Message":" درخواست Token با موفقیت انجام شد"}
[Wed Mar 13 02:55:34 2024] [VerificationCode] {"VerificationCodeId":634481796,"IsSuccessful":true,"Message":"your verification code is sent"}

View File

@@ -0,0 +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!'
})
})
}

View File

@@ -0,0 +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
})
}
}

View File

@@ -0,0 +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()
}

View File

@@ -0,0 +1,47 @@
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'
})
}
}

View File

@@ -0,0 +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: 'خطایی در عملیات مورد نظر رخ داده است.'
})
})
}

View File

@@ -0,0 +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)
}

View File

@@ -0,0 +1,53 @@
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'
})
}
}

View File

@@ -0,0 +1,50 @@
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'
})
}
}

View File

@@ -0,0 +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()
}

View File

@@ -0,0 +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;

View File

@@ -0,0 +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);
module.exports = mongoose.model('AcademyCategory', AcademyCategorySchema);

View File

@@ -0,0 +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);
module.exports = PostModel;

View File

@@ -0,0 +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;

View File

@@ -0,0 +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;

View File

@@ -0,0 +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;

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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: true
},
comment: {
type: String,
trim: true
},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post',
required: false
},
comment_for: {
type: String,
enum: ['user', 'project', 'post'],
required: true
},
status: {
type: String,
enum: ['pending', 'accepted', 'rejected'],
default: 'pending'
}
})
commentSchema.plugin(timestamp)
commentSchema.plugin(mongoosePaginate)
const CommentModel = mongoose.model('Comment', commentSchema)
module.exports = CommentModel

View File

@@ -0,0 +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;

View File

@@ -0,0 +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;

View File

@@ -0,0 +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;

View File

@@ -0,0 +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

View File

@@ -0,0 +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;

View File

@@ -0,0 +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

View File

@@ -0,0 +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
}
})
const MessageModel = mongoose.model('Message', messageSchema)
module.exports = MessageModel

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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);
module.exports = PostModel;

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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 }

View File

@@ -0,0 +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;

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

@@ -0,0 +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

View File

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

View File

@@ -0,0 +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

View File

@@ -0,0 +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