This commit is contained in:
payacom
2026-10-09 15:44:47 +03:30
parent 367416d419
commit e5dcd0f1b2
14 changed files with 282 additions and 197 deletions

View File

@@ -197,7 +197,7 @@ const academyProfile = async (req, res, next) => {
})
}
const { profile_image } = req.files
const profile_image = req.file
const { name, sheba, bio, tag } = req.body
if (!profile_image) {
@@ -227,7 +227,7 @@ const academyProfile = async (req, res, next) => {
}
const uniqueFileName = `${name}-${Date.now()}${path.extname(
profile_image.name
profile_image.originalname
)}`
const filePath = path.join(uploadDir, uniqueFileName)
@@ -329,7 +329,7 @@ const creatCourse = async (req, res, next) => {
const isFreePackage = is_free === 'true' || is_free === true
const contactInfo = parseCourseContactInfo(req.body)
const { course_image } = req.files
const course_image = req.file
if (!course_image) {
return res.status(422).json({
@@ -344,7 +344,7 @@ const creatCourse = async (req, res, next) => {
}
const uniqueFileName = `${cuorse_name}-${Date.now()}${path.extname(
course_image.name
course_image.originalname
)}`
const filePath = path.join(uploadDir, uniqueFileName)
@@ -422,7 +422,7 @@ const updateCourse = async (req, res, next) => {
}
// ========== آپلود عکس جدید (اختیاری) ==========
const { course_image } = req.files || {}
const course_image = req.file
if (course_image) {
// فقط اگه عکس جدید آپلود شده بود، آپلود کن
@@ -433,7 +433,7 @@ const updateCourse = async (req, res, next) => {
// استفاده از id به جای نام دوره برای نام فایل
const safeFileName = `course-${courseId}-${Date.now()}${path.extname(
course_image.name
course_image.originalname
)}`
const filePath = path.join(uploadDir, safeFileName)

View File

@@ -60,12 +60,13 @@ const createAdvertisingValidationRules = () => {
check('city').notEmpty().withMessage('شهر نمی‌تواند خالی باشد')
]
}
// تابع کمکی برای ذخیره تصاویر
// تابع کمکی برای ذخیره تصاویر — file یه آبجکتِ multer‌ه (originalname/path)، نه
// express-fileupload (name/path)
const saveFile = async (file, folder, userName) => {
const uploadDir = path.join(__dirname, '../../../../storage', folder)
if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true })
const uniqueFileName = `${userName}-${Date.now()}${Math.random().toString().slice(2, 11)}${path.extname(file.name)}`
const uniqueFileName = `${userName}-${Date.now()}${Math.random().toString().slice(2, 11)}${path.extname(file.originalname)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(file.path, filePath)
return `/${folder}/${uniqueFileName}`
@@ -129,15 +130,16 @@ const createAdvertising = async (req, res, next) => {
return res.status(422).json({ error: true, message: 'فرمت خدمات اشتباه است' })
}
// multer (routes/application/advertising/index.js) با .any() همه‌ی فایل‌ها رو
// به‌صورتِ آرایه می‌ده (چون serviceImages[id] اسمِ فیلدش به‌ازای هر خدمت پویاست)
const uploadedFiles = Array.isArray(req.files) ? req.files : []
const servicesWithImages = []
for (const service of parsedServices) {
const serviceImages = []
const serviceId = service.id
const serviceFiles = req.files?.serviceImages?.[serviceId]
const imageFiles = Array.isArray(serviceFiles) ? serviceFiles : serviceFiles ? [serviceFiles] : []
for (const file of imageFiles) {
const imageUrl = await saveFile(file, 'services', user.user_name)
const serviceFile = uploadedFiles.find((f) => f.fieldname === `serviceImages[${service.id}]`)
if (serviceFile) {
const imageUrl = await saveFile(serviceFile, 'services', user.user_name)
serviceImages.push(imageUrl)
service.image = imageUrl
}
@@ -145,7 +147,7 @@ const createAdvertising = async (req, res, next) => {
}
// ✅ مدیریت تصاویر تبلیغ
const imageFiles = Array.isArray(req.files?.images) ? req.files.images : req.files?.images ? [req.files.images] : []
const imageFiles = uploadedFiles.filter((f) => f.fieldname === 'images')
const images = []
for (const file of imageFiles) {
const imageUrl = await saveFile(file, 'advertising', user.user_name)
@@ -1337,6 +1339,10 @@ const editAdvertising = async (req, res, next) => {
const provinceFind = await ProvinceModel.findOne({ id: province }, { _id: 0 })
const cityFind = await CityModel.findOne({ id: city })
// multer (routes/application/advertising/index.js) با .any() همه‌ی فایل‌ها رو
// به‌صورتِ آرایه می‌ده (چون serviceImages[id] اسمِ فیلدش به‌ازای هر خدمت پویاست)
const uploadedFiles = Array.isArray(req.files) ? req.files : []
// مدیریت تصاویر برای خدمات
const servicesWithImages = []
const parsedServices = JSON.parse(services)
@@ -1345,24 +1351,12 @@ const editAdvertising = async (req, res, next) => {
if (typeof service.image === 'string' && service.image.startsWith('/services/')) {
servicesWithImages.push(service)
} else {
const serviceImages = []
const serviceId = service.id
if (req.files && req.files.serviceImages && req.files.serviceImages[serviceId]) {
const imageFiles = Array.isArray(req.files.serviceImages[serviceId]) ? req.files.serviceImages[serviceId] : [req.files.serviceImages[serviceId]]
const uploadDir = path.join(__dirname, '../../../../storage/services')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
for (const imageFile of imageFiles) {
const uniqueFileName = `${user.user_name}-${Date.now()}${Math.random().toString().slice(2, 11)}${path.extname(imageFile.name)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(imageFile.path, filePath)
const imageUrl = `/services/${uniqueFileName}`
serviceImages.push(imageUrl)
service.image = imageUrl
}
const serviceFile = uploadedFiles.find((f) => f.fieldname === `serviceImages[${service.id}]`)
if (serviceFile) {
const imageUrl = await saveFile(serviceFile, 'services', user.user_name)
service.image = imageUrl
}
servicesWithImages.push({ ...service, image: serviceImages[0] })
servicesWithImages.push(service)
}
}
}
@@ -1379,22 +1373,10 @@ const editAdvertising = async (req, res, next) => {
}
// اضافه کردن تصاویر جدید
if (req.files && req.files.images) {
const imageFiles = Array.isArray(req.files.images) ? req.files.images : [req.files.images]
const uploadDir = path.join(__dirname, '../../../../storage/advertising')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
for (const imageFile of imageFiles) {
// بررسی فرمت تصویر
if (imageFile && typeof imageFile === 'object' && imageFile.path) {
const uniqueFileName = `${user.user_name}-${Date.now()}${Math.random().toString().slice(2, 11)}${path.extname(imageFile.name)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(imageFile.path, filePath)
const imageUrl = `/advertising/${uniqueFileName}`
images.push(imageUrl)
}
}
const imageFiles = uploadedFiles.filter((f) => f.fieldname === 'images')
for (const file of imageFiles) {
const imageUrl = await saveFile(file, 'advertising', user.user_name)
images.push(imageUrl)
}
const parsedFeatures = JSON.parse(features)
@@ -1421,8 +1403,8 @@ const editAdvertising = async (req, res, next) => {
await advertising.save()
if (contactInfo && contactInfo.saveInfoForNextAds) {
user.contactInfo = contactInfo
await user.save()
// updateOne اتمیک به‌جای user.save() — همون دلیلِ فیکسِ setProfileImageController.js
await UserModel.updateOne({ _id: user._id }, { contactInfo })
}
res.status(200).json({
message: 'ویترین با موفقیت ویرایش شد',
@@ -1646,7 +1628,7 @@ const updateUserAdvertisingProfile = async (req, res, next) => {
})
}
const { profile_image } = req.files
const profile_image = req.file
const {
// profile_image,
vitrine_name,
@@ -1670,7 +1652,7 @@ const updateUserAdvertisingProfile = async (req, res, next) => {
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
const uniqueFileName = `${advertisingProfile?.vitrine_name}-${Date.now()}${path.extname(profile_image.name)}`
const uniqueFileName = `${advertisingProfile?.vitrine_name}-${Date.now()}${path.extname(profile_image.originalname)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(profile_image.path, filePath)
updateFields.profile_image = `/profiles/${uniqueFileName}`

View File

@@ -42,10 +42,10 @@ function productError (res, status, message) {
async function saveFile (file, folder, prefix) {
const uploadDir = path.join(__dirname, '../../../storage', folder)
fs.ensureDirSync(uploadDir)
const uniqueFileName = `${prefix}-${Date.now()}${Math.random().toString().slice(2, 9)}${path.extname(file.name)}`
const uniqueFileName = `${prefix}-${Date.now()}${Math.random().toString().slice(2, 9)}${path.extname(file.originalname)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(file.path, filePath, { overwrite: true })
if ((file.mimetype || file.type || '').startsWith('image/')) {
if ((file.mimetype || '').startsWith('image/')) {
await optimizeImageFile(filePath)
}
return `/${folder}/${uniqueFileName}`
@@ -132,11 +132,7 @@ const createShopProduct = async (req, res, next) => {
})
}
const imageFiles = Array.isArray(req.files?.images)
? req.files.images
: req.files?.images
? [req.files.images]
: []
const imageFiles = Array.isArray(req.files) ? req.files : []
const images = []
for (const file of imageFiles) {
@@ -232,11 +228,7 @@ const updateShopProduct = async (req, res, next) => {
keptImages = Array.isArray(existing) ? existing : []
}
const imageFiles = Array.isArray(req.files?.images)
? req.files.images
: req.files?.images
? [req.files.images]
: []
const imageFiles = Array.isArray(req.files) ? req.files : []
const newImages = []
for (const file of imageFiles) {
const url = await saveFile(file, 'shop-products', shop._id)

View File

@@ -22,14 +22,13 @@ async function findOwnedSubUser (ownerId, subUserId) {
return subUser
}
async function saveAvatarIfProvided (user, files) {
const profile_image = files?.profile_image
async function saveAvatarIfProvided (user, profile_image) {
if (!profile_image) return
const uploadDir = path.join(__dirname, '../../../storage/profiles')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
const uniqueFileName = `${user.user_name}-${Date.now()}${path.extname(profile_image.name)}`
const uniqueFileName = `${user.user_name}-${Date.now()}${path.extname(profile_image.originalname)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(profile_image.path, filePath)
await optimizeImageFile(filePath, { maxDimension: AVATAR_MAX_DIMENSION })
@@ -78,7 +77,7 @@ const createSubUser = async (req, res, next) => {
sub_user_permissions: sanitizeSubUserPermissions(permissions)
})
await saveAvatarIfProvided(subUser, req.files)
await saveAvatarIfProvided(subUser, req.file)
await subUser.save()
return res.json({
@@ -167,7 +166,7 @@ const updateSubUser = async (req, res, next) => {
subUser.sub_user_permissions = sanitizeSubUserPermissions(permissions)
}
await saveAvatarIfProvided(subUser, req.files)
await saveAvatarIfProvided(subUser, req.file)
await subUser.save()
return res.json({

View File

@@ -74,7 +74,7 @@ const addUserMessageToTicket = async (req, res) => {
const { ticketId } = req.query
const { text } = req.body
const { file } = req.files
const file = req.file
let fileUrl = null
if (file) {
// اگر فایل ارسال شده است، آن را ذخیره کنید
@@ -83,8 +83,8 @@ const addUserMessageToTicket = async (req, res) => {
fs.mkdirSync(uploadDir, { recursive: true })
}
// اسمِ فایل هیچ‌وقت مستقیم از ورودیِ کاربر (file.name) گرفته نمی‌شه — فقط پسوندش
const uniqueFileName = `${Date.now()}-${crypto.randomUUID()}${path.extname(file.name || '')}`
// اسمِ فایل هیچ‌وقت مستقیم از ورودیِ کاربر (file.originalname) گرفته نمی‌شه — فقط پسوندش
const uniqueFileName = `${Date.now()}-${crypto.randomUUID()}${path.extname(file.originalname || '')}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(file.path, filePath)

View File

@@ -1,8 +1,6 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const fs = require('fs-extra')
const path = require('path')
const setServices = async (req, res, next) => {
try {
@@ -10,13 +8,6 @@ const setServices = async (req, res, next) => {
if (!token) return res.status(401).send('Access Denied')
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const user = await UserModel.findById(decodedToken.id)
if (!user) {
return res.status(422).json({
error: true,
message: 'شما دسترسی به این بخش ندارید'
})
}
const { services } = req.body
if (!services) {
return res.status(422).json({
@@ -25,38 +16,37 @@ const setServices = async (req, res, next) => {
})
}
// مدیریت تصاویر برای خدمات
const servicesWithImages = []
// مدیریت تصاویر برای خدمات — multer (routes/application/verify/index.js، با
// .any() چون اسمِ فیلد به ازای هر خدمت پویاست: serviceImages[id]) فایل‌ها رو
// به‌صورتِ آرایه می‌ده، با fieldname مشخص می‌کنیم مالِ کدوم خدمته
const parsedServices = JSON.parse(services) // پارس کردن رشته JSON به آرایه جاوا اسکریپت
if (parsedServices && parsedServices.length > 0) {
for (const service of parsedServices) {
const serviceImages = []
const serviceId = service.id
if (req.files && req.files.serviceImages && req.files.serviceImages[serviceId]) {
const imageFiles = Array.isArray(req.files.serviceImages[serviceId]) ? req.files.serviceImages[serviceId] : [req.files.serviceImages[serviceId]]
const uploadDir = path.join(__dirname, '../../../../storage/services')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
for (const imageFile of imageFiles) {
const uniqueFileName = `${user.user_name}-${Date.now()}${Math.random().toString().slice(2, 11)}${path.extname(imageFile.name)}`
const filePath = path.join(uploadDir, uniqueFileName)
// await fs.promises.rename(imageFile.path, filePath)
await fs.move(imageFile.path, filePath)
const imageUrl = `/services/${uniqueFileName}`
serviceImages.push(imageUrl)
// به‌روزرسانی مسیر تصویر در آبجکت service
service.image = imageUrl
}
} else {
console.log('sss')
}
servicesWithImages.push({ ...service, images: serviceImages })
const uploadedFiles = Array.isArray(req.files) ? req.files : []
const servicesWithImages = (parsedServices || []).map((service) => {
const uploaded = uploadedFiles.find(
(f) => f.fieldname === `serviceImages[${service.id}]`
)
if (uploaded) {
const imageUrl = `/services/${uploaded.filename}`
return { ...service, image: imageUrl, images: [imageUrl] }
}
}
// بدون عکسِ تازه — هر مسیرِ عکسِ قبلی که خودِ کلاینت توی همین آیتم فرستاده
// (حالتِ ویرایش، نگاه کن به EditServicesScreen.tsx/web) دست‌نخورده می‌مونه
return { ...service, images: service.image ? [service.image] : [] }
})
user.services = servicesWithImages
await user.save()
// updateOne اتمیک به‌جای findById+user.save() — یه enum نامعتبرِ قدیمی روی یه
// فیلدِ کاملاً بی‌ربطِ همین کاربر با save() کلِ سند رو re-validate می‌کرد و همین
// آپدیتِ بی‌ربط رو می‌شکست (نگاه کن به همین فیکس توی setProfileImageController.js)
const result = await UserModel.updateOne(
{ _id: decodedToken.id },
{ services: servicesWithImages }
)
if (result.matchedCount === 0) {
return res.status(422).json({
error: true,
message: 'شما دسترسی به این بخش ندارید'
})
}
return res.json({
message: 'خدمات با موفقیت ثبت شد'

View File

@@ -1,6 +1,4 @@
/* eslint-disable camelcase */
const fs = require('fs-extra')
const path = require('path')
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
@@ -8,16 +6,8 @@ const setNationalCardImage = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { national_card_image } = req.files
// جستجوی کاربر با شماره موبایل ارسالی
const national_card_image = req.file
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const user = await UserModel.findById(decodedToken.id)
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
// بررسی آیا فایل آپلود شده است
if (!national_card_image) {
return res.status(400).json({
@@ -26,25 +16,27 @@ const setNationalCardImage = async (req, res, next) => {
})
}
// ذخیره فایل عکس پروفایل
const uploadDir = path.join(__dirname, '../../../../storage/carts')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
// multer (routes/application/verify/index.js) فایل رو مستقیم با اسمِ یکتا توی
// storage/carts ذخیره کرده — نیازی به move جداگانه نیست
const imagePath = `/carts/${national_card_image.filename}`
// updateOne اتمیک به‌جای findById+user.save() — یه enum نامعتبرِ قدیمی روی یه
// فیلدِ بی‌ربطِ همین کاربر با save() کلِ سند رو re-validate و همین آپدیت رو
// می‌شکست (نگاه کن به همین فیکس توی setProfileImageController.js)
const result = await UserModel.updateOne(
{ _id: decodedToken.id },
{ national_card_image: imagePath, is_Register: true, is_verified: 'pending' }
)
if (result.matchedCount === 0) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
const uniqueFileName = `${user.user_name}-${Date.now()}${path.extname(national_card_image.name)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(national_card_image.path, filePath)
// ذخیره مسیر فایل در دیتابیس
user.national_card_image = `/carts/${uniqueFileName}`
user.is_Register = true
user.is_verified = 'pending'
await user.save()
return res.json({
message: 'عکس کارت ملی با موفقیت ذخیره شد',
national_card_image: user.national_card_image
national_card_image: imagePath
})
} catch (error) {
next(error)

View File

@@ -1,6 +1,4 @@
/* eslint-disable camelcase */
const fs = require('fs-extra')
const path = require('path')
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const { optimizeImageFile } = require('../../../utils/imageOptimize')
@@ -13,15 +11,8 @@ const setProfileImage = async (req, res, next) => {
try {
const token = req.header('Authorization').split(' ')[1]
if (!token) return res.status(401).send('Access Denied')
const { profile_image } = req.files
const profile_image = req.file
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const user = await UserModel.findById(decodedToken.id)
if (!user) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
// بررسی آیا فایل آپلود شده است
if (!profile_image) {
return res.status(400).json({
@@ -30,23 +21,28 @@ const setProfileImage = async (req, res, next) => {
})
}
// ذخیره فایل عکس پروفایل
const uploadDir = path.join(__dirname, '../../../storage/profiles')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
const uniqueFileName = `${user.user_name}-${Date.now()}${path.extname(profile_image.name)}`
const filePath = path.join(uploadDir, uniqueFileName)
// multer (routes/application/verify/index.js) فایل رو مستقیم با اسمِ یکتا توی
// storage/profiles ذخیره کرده — نیازی به move جداگانه نیست
await optimizeImageFile(profile_image.path, { maxDimension: AVATAR_MAX_DIMENSION })
const imagePath = `/profiles/${profile_image.filename}`
await fs.move(profile_image.path, filePath)
await optimizeImageFile(filePath, { maxDimension: AVATAR_MAX_DIMENSION })
// ذخیره مسیر فایل در دیتابیس
user.profile_image = `/profiles/${uniqueFileName}`
await user.save()
// updateOne اتمیک به‌جای findById+user.save() — یه enum نامعتبرِ قدیمی روی یه
// فیلدِ کاملاً بی‌ربطِ همین کاربر (مثلاً personal_style، از داده‌های قدیمی) با
// save() کلِ سند رو re-validate می‌کرد و همین آپدیتِ بی‌ربط رو می‌شکست
const result = await UserModel.updateOne(
{ _id: decodedToken.id },
{ profile_image: imagePath }
)
if (result.matchedCount === 0) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
return res.json({
message: 'عکس پروفایل با موفقیت ذخیره شد',
profile_image: user.profile_image
profile_image: imagePath
})
} catch (error) {
next(error)
@@ -59,7 +55,7 @@ const updateProfileImage = async (req, res, next) => {
if (!token) return res.status(401).send('Access Denied')
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
const userId = decodedToken.id
const { profile_image } = req.files
const profile_image = req.file
if (!profile_image) {
return res.status(422).json({
@@ -68,33 +64,23 @@ const updateProfileImage = async (req, res, next) => {
})
}
const user = await UserModel.findById(userId)
// multer (routes/application/verify/index.js) فایل رو مستقیم با اسمِ یکتا توی
// storage/profiles ذخیره کرده — نیازی به move جداگانه نیست
await optimizeImageFile(profile_image.path, { maxDimension: AVATAR_MAX_DIMENSION })
const imagePath = `/profiles/${profile_image.filename}`
if (!user) {
// updateOne اتمیک به‌جای findById+user.save() — همون دلیلِ setProfileImage بالا
const result = await UserModel.updateOne({ _id: userId }, { profile_image: imagePath })
if (result.matchedCount === 0) {
return res.status(404).json({
error: true,
message: 'کاربری با این شماره موبایل یافت نشد'
})
}
const uploadDir = path.join(__dirname, '../../../storage/profiles')
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true })
}
const uniqueFileName = `${user.user_name}-${Date.now()}${path.extname(profile_image.name)}`
const filePath = path.join(uploadDir, uniqueFileName)
await fs.move(profile_image.path, filePath)
await optimizeImageFile(filePath, { maxDimension: AVATAR_MAX_DIMENSION })
user.profile_image = `/profiles/${uniqueFileName}`
await user.save()
return res.json({
message: 'عکس پروفایل با موفقیت به‌روزرسانی شد',
profile_image: user.profile_image
profile_image: imagePath
})
} catch (error) {
next(error)

View File

@@ -74,18 +74,19 @@ router.get("/", academyController.findAcademy);
// یافتن آکادمی با شناسه
router.post("/findAcademyById", academyController.findAcademyById);
// تکمیل پروفایل آکادمی
router.post("/academy/profile", academyController.academyProfile);
// تکمیل پروفایل آکادمی — همون باگِ req.files که توی verify/* پیدا شد (middlewareِ
// global قدیمی که req.files رو پر می‌کرد حذف شده بود، این روت بدونِ جایگزین مونده بود)
router.post("/academy/profile", upload.single("profile_image"), academyController.academyProfile);
// ==========================================
// 📚 مسیرهای مدیریت دوره‌ها (Courses)
// ==========================================
// ایجاد دوره جدید
router.post("/academy/creat/course", [auth, blockCheck, requireSubUserPermission('academy_packages')], academyController.creatCourse);
// ایجاد دوره جدید — همون باگِ req.files بالا
router.post("/academy/creat/course", [auth, blockCheck, requireSubUserPermission('academy_packages')], upload.single("course_image"), academyController.creatCourse);
// ویرایش دوره
router.patch("/academy/update/course", [auth, blockCheck, requireSubUserPermission('academy_packages')], academyController.updateCourse);
// ویرایش دوره — همون باگِ req.files بالا
router.patch("/academy/update/course", [auth, blockCheck, requireSubUserPermission('academy_packages')], upload.single("course_image"), academyController.updateCourse);
// حذف دوره (تغییر وضعیت به reject)
router.delete("/academy/delete/course", academyController.deleteCourse);

View File

@@ -1,4 +1,7 @@
const express = require('express')
const multer = require('multer')
const fs = require('fs')
const path = require('path')
const blockCheck = require('../../../middlewares/blockCheck')
const { createAdvertisingValidationRules, createAdvertising, getAdvertisingTypes, getAdvertisingCategory, getSingleAdvertising, getAdvertisingFeatures, getAdvertisings, toggleLike, addComment, getComments, rateAdvertising, getUserAdvertisings, getAdvertisingAnalytics, editAdvertising, getUserSingleAdvertising, getUserAdvertisingProfile, updateUserAdvertisingProfile, getAdvertisingsWeb, getSingleAdvertisingWeb, getUserAdvertisingProfileWeb, recordAdvertisingView, getUserLikedAdvertisings, getUserAdvertisingComments } = require('../../../controllers/application/advertising/advertisingController')
const auth = require('../../../middlewares/auth')
@@ -6,16 +9,38 @@ const requireSubUserPermission = require('../../../middlewares/requireSubUserPer
const { initiateAdvertisingPayment, handleAdvertisingPaymentCallback, handleAdvertisingRepublishPaymentCallback, republishAdvertisingPayment } = require('../../../controllers/application/payment/paymentController')
const { initiateAdvertisingPaymentWeb, handleAdvertisingPaymentCallbackWeb, republishAdvertisingPaymentWeb, handleAdvertisingRepublishPaymentCallbackWeb } = require('../../../controllers/application/payment/paymentControllerWeb')
const router = express.Router()
// قبلاً express-form-data به‌صورتِ global این فیلدها رو تویِ req.files پر می‌کرد؛
// وقتی اون middleware حذف شد (تداخل با multer توی posts/chat)، این روت‌ها بدونِ هیچ
// parserِ جایگزینی موندن — یعنی آپلودِ عکسِ بیلبورد/خدمات/پروفایلِ ویترین همیشه
// خاموش بودن (createAdvertising حتی بدون خطا، فقط عکس‌ها رو نادیده می‌گرفت)
const advertisingTmpDir = '/tmp/modstagram-uploads'
if (!fs.existsSync(advertisingTmpDir)) {
fs.mkdirSync(advertisingTmpDir, { recursive: true })
}
const advertisingTmpUpload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => cb(null, advertisingTmpDir),
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${Math.round(Math.random() * 1e9)}${path.extname(file.originalname || '')}`)
}
}),
limits: { fileSize: 50 * 1024 * 1024 }
})
// اسمِ فیلدِ عکسِ هر خدمت پویاست (serviceImages[id])، پس .any() به‌جای .fields()
// با لیستِ ثابت — کنترلر با file.fieldname تشخیص می‌ده مالِ کدوم خدمت/بخشه
const advertisingImagesUpload = advertisingTmpUpload.any()
const advertisingProfileImageUpload = advertisingTmpUpload.single('profile_image')
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.patch('/profile', [auth], [blockCheck], advertisingProfileImageUpload, updateUserAdvertisingProfile)
router.get('/user-advertising', [auth], getUserAdvertisings)
router.get('/analytics', [auth], getAdvertisingAnalytics)
router.get('/user-liked', [auth], getUserLikedAdvertisings)
router.get('/user-comments', [auth], getUserAdvertisingComments)
router.post('/create', [auth], [blockCheck], requireSubUserPermission('billboards'), createAdvertisingValidationRules(), createAdvertising)
router.post('/create', [auth], [blockCheck], requireSubUserPermission('billboards'), advertisingImagesUpload, createAdvertisingValidationRules(), createAdvertising)
router.get('/types', [auth], getAdvertisingTypes)
router.get('/categories', getAdvertisingCategory)
router.get('/features', [auth], getAdvertisingFeatures)
@@ -35,6 +60,6 @@ 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], requireSubUserPermission('billboards'), editAdvertising)
router.post('/edit/:id', [blockCheck], [auth], requireSubUserPermission('billboards'), advertisingImagesUpload, editAdvertising)
module.exports = router

View File

@@ -1,6 +1,26 @@
const express = require('express')
const multer = require('multer')
const fs = require('fs')
const path = require('path')
const blockCheck = require('../../../middlewares/blockCheck')
const requireSubUserPermission = require('../../../middlewares/requireSubUserPermission')
// قبلاً express-form-data به‌صورتِ global این فیلد رو تویِ req.files پر می‌کرد؛ وقتی
// اون middleware حذف شد (تداخل با multer توی posts/chat)، این روت‌ها بدونِ هیچ parserِ
// جایگزینی موندن — یعنی آپلودِ عکسِ محصول همیشه خاموش بود
const shopProductsTmpDir = '/tmp/modstagram-uploads'
if (!fs.existsSync(shopProductsTmpDir)) {
fs.mkdirSync(shopProductsTmpDir, { recursive: true })
}
const shopProductImagesUpload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => cb(null, shopProductsTmpDir),
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${Math.round(Math.random() * 1e9)}${path.extname(file.originalname || '')}`)
}
}),
limits: { fileSize: 50 * 1024 * 1024 }
}).array('images', 10)
const {
searchCatalog,
createShopProduct,
@@ -25,8 +45,8 @@ router.get('/:id', getShopProductById)
router.get('/:id/comments', getShopProductComments)
// Seller-only
router.post('/', [blockCheck], requireSubUserPermission('shop_products'), createShopProduct)
router.patch('/:id', [blockCheck], requireSubUserPermission('shop_products'), updateShopProduct)
router.post('/', [blockCheck], requireSubUserPermission('shop_products'), shopProductImagesUpload, createShopProduct)
router.patch('/:id', [blockCheck], requireSubUserPermission('shop_products'), shopProductImagesUpload, updateShopProduct)
// Authenticated buyer actions
router.post('/:id/comments', [blockCheck], createShopProductComment)

View File

@@ -1,8 +1,28 @@
const express = require('express')
const multer = require('multer')
const fs = require('fs')
const path = require('path')
const router = express.Router()
const auth = require('../../../middlewares/auth')
const blockCheck = require('../../../middlewares/blockCheck')
// قبلاً express-form-data به‌صورتِ global این فیلد رو تویِ req.files پر می‌کرد؛ وقتی
// اون middleware حذف شد (تداخل با multer توی posts/chat)، این روت‌ها بدونِ هیچ parserِ
// جایگزینی موندن — یعنی آپلودِ آواتارِ کاربرِ زیرمجموعه همیشه خاموش بود
const subUserTmpDir = '/tmp/modstagram-uploads'
if (!fs.existsSync(subUserTmpDir)) {
fs.mkdirSync(subUserTmpDir, { recursive: true })
}
const subUserAvatarUpload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => cb(null, subUserTmpDir),
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${Math.round(Math.random() * 1e9)}${path.extname(file.originalname || '')}`)
}
}),
limits: { fileSize: 20 * 1024 * 1024 }
}).single('profile_image')
const {
createSubUser,
listSubUsers,
@@ -23,10 +43,10 @@ function ownerOnly (req, res, next) {
next()
}
router.post('/', [auth, blockCheck, ownerOnly], createSubUser)
router.post('/', [auth, blockCheck, ownerOnly], subUserAvatarUpload, createSubUser)
router.get('/', [auth, blockCheck, ownerOnly], listSubUsers)
router.get('/:id', [auth, blockCheck, ownerOnly], getSubUser)
router.patch('/:id', [auth, blockCheck, ownerOnly], updateSubUser)
router.patch('/:id', [auth, blockCheck, ownerOnly], subUserAvatarUpload, updateSubUser)
router.patch('/:id/active', [auth, blockCheck, ownerOnly], setSubUserActive)
module.exports = router

View File

@@ -1,9 +1,30 @@
const express = require('express')
const multer = require('multer')
const fs = require('fs')
const path = require('path')
const { getUserTickets, createTicket, addUserMessageToTicket, getTicketMessages } = require('../../../controllers/application/tickets/ticketsController')
const router = express.Router()
// قبلاً express-form-data به‌صورتِ global این فیلد رو تویِ req.files پر می‌کرد؛ وقتی
// اون middleware حذف شد (تداخل با multer توی posts/chat)، این روت بدونِ هیچ parserِ
// جایگزینی مونده بود — یعنی پیوستِ فایلِ پیامِ تیکت همیشه خاموش بود
const ticketTmpDir = '/tmp/modstagram-uploads'
if (!fs.existsSync(ticketTmpDir)) {
fs.mkdirSync(ticketTmpDir, { recursive: true })
}
const ticketFileUpload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => cb(null, ticketTmpDir),
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${Math.round(Math.random() * 1e9)}${path.extname(file.originalname || '')}`)
}
}),
limits: { fileSize: 50 * 1024 * 1024 }
}).single('file')
router.get('/', getUserTickets)
router.get('/messages', getTicketMessages)
router.post('/create', createTicket)
router.post('/message', addUserMessageToTicket)
router.post('/message', ticketFileUpload, addUserMessageToTicket)
module.exports = router

View File

@@ -1,4 +1,8 @@
const express = require('express')
const multer = require('multer')
const crypto = require('crypto')
const fs = require('fs')
const path = require('path')
const { setProfileImage, updateProfileImage } = require('../../../controllers/application/verify/setProfileImageController')
const { setExpertise, updateExpertise } = require('../../../controllers/application/verify/expertiseController')
const { setGender, updateGender } = require('../../../controllers/application/verify/genderController')
@@ -16,16 +20,54 @@ const { setServices, updateServices } = require('../../../controllers/applicatio
const { setPersonalDetails, updatePersonalDetails } = require('../../../controllers/application/verify/personalDetailsController')
const router = express.Router()
router.post('/profile_image', setProfileImage)
router.patch('/profile_image', [blockCheck], [auth], updateProfileImage)
// قبلاً express-form-data به‌صورتِ global این فیلدها رو تویِ req.files پر می‌کرد؛
// وقتی اون middleware حذف شد (تداخل با multer توی posts/chat)، این روت بدونِ هیچ
// parserِ جایگزینی موند — یعنی req.files همیشه undefined بود و هر آپلودِ عکسِ
// پروفایل با «خطایی رخ داده است» شکست می‌خورد (همون الگویی که قبلاً برای
// /api/v1/chat/file پیدا و با multer.diskStorage رفع شد — نگاه کن به index.js)
const profileImageDir = path.join(__dirname, '../../../storage/profiles')
if (!fs.existsSync(profileImageDir)) {
fs.mkdirSync(profileImageDir, { recursive: true })
}
const profileImageUpload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => cb(null, profileImageDir),
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${crypto.randomUUID()}${path.extname(file.originalname || '')}`)
}
}),
limits: { fileSize: 20 * 1024 * 1024 }
}).single('profile_image')
router.post('/profile_image', profileImageUpload, setProfileImage)
router.patch('/profile_image', [blockCheck], [auth], profileImageUpload, updateProfileImage)
router.post('/expertise', setExpertise)
router.patch('/expertise', [blockCheck], [auth], updateExpertise)
router.post('/gender', setGender)
router.patch('/gender', [blockCheck], [auth], updateGender)
router.post('/sizes', setSizes)
router.patch('/sizes', [blockCheck], [auth], updateSizes)
router.post('/services', setServices)
router.patch('/services', [blockCheck], [auth], updateServices)
// همون باگِ req.files (نگاه کن به کامنتِ profileImageUpload بالا) اینجا هم بود؛
// فرق با بقیه اینه که اسمِ فیلدِ عکسِ هر خدمت پویاست (serviceImages[serviceId]،
// یه آیدی متفاوت برای هر خدمت) پس به‌جای .single()/.fields() با اسمِ ثابت، از
// .any() استفاده می‌شه — همه‌ی فایل‌ها با هر اسمی قبول می‌شه، بعد توی کنترلر با
// file.fieldname تطبیق داده می‌شه
const servicesImagesDir = path.join(__dirname, '../../../storage/services')
if (!fs.existsSync(servicesImagesDir)) {
fs.mkdirSync(servicesImagesDir, { recursive: true })
}
const servicesUpload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => cb(null, servicesImagesDir),
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${crypto.randomUUID()}${path.extname(file.originalname || '')}`)
}
}),
limits: { fileSize: 20 * 1024 * 1024 }
}).any()
router.post('/services', servicesUpload, setServices)
router.patch('/services', [blockCheck], [auth], servicesUpload, updateServices)
router.post('/colors', setColors)
router.patch('/colors', [blockCheck], [auth], updateColors)
router.post('/personal_details', [blockCheck], [auth], setPersonalDetails)
@@ -38,7 +80,22 @@ 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)
// همون باگِ req.files (نگاه کن به کامنتِ profileImageUpload بالا) اینجا هم بود
const nationalCardDir = path.join(__dirname, '../../../storage/carts')
if (!fs.existsSync(nationalCardDir)) {
fs.mkdirSync(nationalCardDir, { recursive: true })
}
const nationalCardUpload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => cb(null, nationalCardDir),
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${crypto.randomUUID()}${path.extname(file.originalname || '')}`)
}
}),
limits: { fileSize: 20 * 1024 * 1024 }
}).single('national_card_image')
router.post('/national_card_image', nationalCardUpload, setNationalCardImage)
router.post('/complete', completeRegistration)
module.exports = router