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

View File

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