reels
This commit is contained in:
220
controllers/application/subUsers/subUserController.js
Normal file
220
controllers/application/subUsers/subUserController.js
Normal file
@@ -0,0 +1,220 @@
|
||||
/* eslint-disable camelcase */
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const bcrypt = require('bcryptjs')
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const { validateUsername } = require('../../../utils/usernameValidation')
|
||||
const { optimizeImageFile } = require('../../../utils/imageOptimize')
|
||||
const { sanitizeSubUserPermissions } = require('../../../utils/subUserPermissions')
|
||||
|
||||
const AVATAR_MAX_DIMENSION = 800
|
||||
const PASSWORD_REGEX = /^(?=.*[a-zA-Zآ-ی])(?=.*\d).{8,}$/u
|
||||
|
||||
// یه کاربرِ زیرمجموعه فقط باید صاحبِ خودش رو ببینه/ویرایش کنه — نه سایرِ
|
||||
// زیرمجموعههای حسابهای دیگه، و خودِ یه زیرمجموعه هم اصلاً نباید بتونه این
|
||||
// مسیرها رو صدا بزنه (چک با req.actingSubUser تو خودِ روت انجام میشه)
|
||||
async function findOwnedSubUser (ownerId, subUserId) {
|
||||
const subUser = await UserModel.findOne({
|
||||
_id: subUserId,
|
||||
parent_account_id: ownerId,
|
||||
is_sub_user: true
|
||||
})
|
||||
return subUser
|
||||
}
|
||||
|
||||
async function saveAvatarIfProvided (user, files) {
|
||||
const profile_image = files?.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 filePath = path.join(uploadDir, uniqueFileName)
|
||||
await fs.move(profile_image.path, filePath)
|
||||
await optimizeImageFile(filePath, { maxDimension: AVATAR_MAX_DIMENSION })
|
||||
user.profile_image = `/profiles/${uniqueFileName}`
|
||||
}
|
||||
|
||||
const createSubUser = async (req, res, next) => {
|
||||
try {
|
||||
const { first_name, last_name, user_name, password } = req.body
|
||||
let permissions = req.body.permissions
|
||||
// فرمدیتا (چون همراه با آپلود عکسه) ممکنه permissions رو بهصورت رشتهی JSON بفرسته
|
||||
if (typeof permissions === 'string') {
|
||||
try { permissions = JSON.parse(permissions) } catch { permissions = [] }
|
||||
}
|
||||
|
||||
if (!first_name || !first_name.trim()) {
|
||||
return res.status(422).json({ error: true, message: 'نام الزامی است' })
|
||||
}
|
||||
|
||||
const usernameError = validateUsername(user_name)
|
||||
if (usernameError) {
|
||||
return res.status(422).json({ error: true, message: usernameError })
|
||||
}
|
||||
|
||||
if (!password || !PASSWORD_REGEX.test(password)) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'رمز عبور باید حداقل ۸ کاراکتر و شامل حروف و اعداد باشد'
|
||||
})
|
||||
}
|
||||
|
||||
const normalizedUsername = user_name.trim().toLowerCase()
|
||||
const existing = await UserModel.findOne({ user_name: normalizedUsername })
|
||||
if (existing) {
|
||||
return res.status(422).json({ error: true, message: 'این نام کاربری قبلاً استفاده شده است' })
|
||||
}
|
||||
|
||||
const subUser = new UserModel({
|
||||
first_name: first_name.trim(),
|
||||
last_name: (last_name || '').trim(),
|
||||
user_name: normalizedUsername,
|
||||
password: await bcrypt.hash(password, 10),
|
||||
parent_account_id: req.user._id,
|
||||
is_sub_user: true,
|
||||
sub_user_active: true,
|
||||
sub_user_permissions: sanitizeSubUserPermissions(permissions)
|
||||
})
|
||||
|
||||
await saveAvatarIfProvided(subUser, req.files)
|
||||
await subUser.save()
|
||||
|
||||
return res.json({
|
||||
message: 'کاربر زیرمجموعه با موفقیت ساخته شد',
|
||||
subUser: serializeSubUser(subUser)
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const listSubUsers = async (req, res, next) => {
|
||||
try {
|
||||
const subUsers = await UserModel.find({
|
||||
parent_account_id: req.user._id,
|
||||
is_sub_user: true
|
||||
}).sort({ createdAt: -1 })
|
||||
|
||||
return res.json({ subUsers: subUsers.map(serializeSubUser) })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const getSubUser = async (req, res, next) => {
|
||||
try {
|
||||
const subUser = await findOwnedSubUser(req.user._id, req.params.id)
|
||||
if (!subUser) {
|
||||
return res.status(404).json({ error: true, message: 'کاربر زیرمجموعه یافت نشد' })
|
||||
}
|
||||
return res.json({ subUser: serializeSubUser(subUser) })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const updateSubUser = async (req, res, next) => {
|
||||
try {
|
||||
const subUser = await findOwnedSubUser(req.user._id, req.params.id)
|
||||
if (!subUser) {
|
||||
return res.status(404).json({ error: true, message: 'کاربر زیرمجموعه یافت نشد' })
|
||||
}
|
||||
|
||||
const { first_name, last_name, user_name, password } = req.body
|
||||
let permissions = req.body.permissions
|
||||
if (typeof permissions === 'string') {
|
||||
try { permissions = JSON.parse(permissions) } catch { permissions = undefined }
|
||||
}
|
||||
|
||||
if (first_name !== undefined) {
|
||||
if (!first_name.trim()) {
|
||||
return res.status(422).json({ error: true, message: 'نام الزامی است' })
|
||||
}
|
||||
subUser.first_name = first_name.trim()
|
||||
}
|
||||
if (last_name !== undefined) {
|
||||
subUser.last_name = last_name.trim()
|
||||
}
|
||||
|
||||
if (user_name !== undefined) {
|
||||
const usernameError = validateUsername(user_name)
|
||||
if (usernameError) {
|
||||
return res.status(422).json({ error: true, message: usernameError })
|
||||
}
|
||||
const normalizedUsername = user_name.trim().toLowerCase()
|
||||
if (normalizedUsername !== subUser.user_name) {
|
||||
const existing = await UserModel.findOne({ user_name: normalizedUsername })
|
||||
if (existing) {
|
||||
return res.status(422).json({ error: true, message: 'این نام کاربری قبلاً استفاده شده است' })
|
||||
}
|
||||
subUser.user_name = normalizedUsername
|
||||
}
|
||||
}
|
||||
|
||||
if (password) {
|
||||
if (!PASSWORD_REGEX.test(password)) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'رمز عبور باید حداقل ۸ کاراکتر و شامل حروف و اعداد باشد'
|
||||
})
|
||||
}
|
||||
subUser.password = await bcrypt.hash(password, 10)
|
||||
}
|
||||
|
||||
if (permissions !== undefined) {
|
||||
subUser.sub_user_permissions = sanitizeSubUserPermissions(permissions)
|
||||
}
|
||||
|
||||
await saveAvatarIfProvided(subUser, req.files)
|
||||
await subUser.save()
|
||||
|
||||
return res.json({
|
||||
message: 'اطلاعات با موفقیت بهروزرسانی شد',
|
||||
subUser: serializeSubUser(subUser)
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const setSubUserActive = async (req, res, next) => {
|
||||
try {
|
||||
const subUser = await findOwnedSubUser(req.user._id, req.params.id)
|
||||
if (!subUser) {
|
||||
return res.status(404).json({ error: true, message: 'کاربر زیرمجموعه یافت نشد' })
|
||||
}
|
||||
|
||||
subUser.sub_user_active = Boolean(req.body.active)
|
||||
await subUser.save()
|
||||
|
||||
return res.json({
|
||||
message: subUser.sub_user_active ? 'کاربر فعال شد' : 'کاربر غیرفعال شد',
|
||||
subUser: serializeSubUser(subUser)
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
function serializeSubUser (subUser) {
|
||||
return {
|
||||
_id: subUser._id,
|
||||
first_name: subUser.first_name,
|
||||
last_name: subUser.last_name,
|
||||
user_name: subUser.user_name,
|
||||
profile_image: subUser.profile_image,
|
||||
sub_user_active: subUser.sub_user_active,
|
||||
sub_user_permissions: subUser.sub_user_permissions || [],
|
||||
createdAt: subUser.createdAt
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createSubUser,
|
||||
listSubUsers,
|
||||
getSubUser,
|
||||
updateSubUser,
|
||||
setSubUserActive
|
||||
}
|
||||
Reference in New Issue
Block a user