feat: add env-based super admin authentication and complete role & permission system
deploy-chabokan / deploy (push) Canceled after 0s
deploy-chabokan / deploy (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,516 @@
|
||||
/**
|
||||
* System-wide Permission Definitions
|
||||
* Organized by functional category for the Belonging Manager platform.
|
||||
*/
|
||||
|
||||
const permissionsList = [
|
||||
// ─── مدیریت کاربران (User Management) ──────────────────────────────────────
|
||||
{
|
||||
englishName: 'View All Users',
|
||||
nativeName: 'مشاهده لیست کاربران',
|
||||
key: 'getallusers',
|
||||
category: 'کاربران',
|
||||
},
|
||||
{
|
||||
englishName: 'View User Details',
|
||||
nativeName: 'مشاهده جزئیات کاربر',
|
||||
key: 'getuser',
|
||||
category: 'کاربران',
|
||||
},
|
||||
{
|
||||
englishName: 'Create User',
|
||||
nativeName: 'ایجاد کاربر جدید',
|
||||
key: 'createuser',
|
||||
category: 'کاربران',
|
||||
},
|
||||
{
|
||||
englishName: 'Update User',
|
||||
nativeName: 'ویرایش کاربر',
|
||||
key: 'updateuser',
|
||||
category: 'کاربران',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete User',
|
||||
nativeName: 'حذف کاربر',
|
||||
key: 'deleteuser',
|
||||
category: 'کاربران',
|
||||
},
|
||||
|
||||
// ─── مدیریت نقشها و دسترسیها (Roles & Permissions) ────────────────────────
|
||||
{
|
||||
englishName: 'View All Roles',
|
||||
nativeName: 'مشاهده لیست نقشها',
|
||||
key: 'getallroles',
|
||||
category: 'نقشها و دسترسیها',
|
||||
},
|
||||
{
|
||||
englishName: 'View Role Details',
|
||||
nativeName: 'مشاهده جزئیات نقش',
|
||||
key: 'getarole',
|
||||
category: 'نقشها و دسترسیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Role',
|
||||
nativeName: 'ایجاد نقش جدید',
|
||||
key: 'createarole',
|
||||
category: 'نقشها و دسترسیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Role',
|
||||
nativeName: 'ویرایش نقش و دسترسیها',
|
||||
key: 'updatearole',
|
||||
category: 'نقشها و دسترسیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Role',
|
||||
nativeName: 'حذف نقش',
|
||||
key: 'deletearole',
|
||||
category: 'نقشها و دسترسیها',
|
||||
},
|
||||
{
|
||||
englishName: 'View All Permissions',
|
||||
nativeName: 'مشاهده لیست دسترسیها',
|
||||
key: 'getallpermissions',
|
||||
category: 'نقشها و دسترسیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Permission',
|
||||
nativeName: 'ایجاد دسترسی',
|
||||
key: 'createpermission',
|
||||
category: 'نقشها و دسترسیها',
|
||||
},
|
||||
{
|
||||
englishName: 'View Permission',
|
||||
nativeName: 'مشاهده دسترسی',
|
||||
key: 'getpermission',
|
||||
category: 'نقشها و دسترسیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Permission',
|
||||
nativeName: 'ویرایش دسترسی',
|
||||
key: 'updatepermission',
|
||||
category: 'نقشها و دسترسیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Permission',
|
||||
nativeName: 'حذف دسترسی',
|
||||
key: 'deletepermission',
|
||||
category: 'نقشها و دسترسیها',
|
||||
},
|
||||
|
||||
// ─── مدیریت املاک و اموال (Property / Belongings) ─────────────────────────
|
||||
{
|
||||
englishName: 'View All Properties',
|
||||
nativeName: 'مشاهده لیست املاک',
|
||||
key: 'getallproperties',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'View Property Details',
|
||||
nativeName: 'مشاهده جزئیات ملک',
|
||||
key: 'getaproperty',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Property',
|
||||
nativeName: 'ایجاد ملک جدید',
|
||||
key: 'createaproperty',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Property',
|
||||
nativeName: 'ویرایش ملک',
|
||||
key: 'updateaproperty',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Property',
|
||||
nativeName: 'حذف ملک',
|
||||
key: 'deleteaproperty',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'Search Properties',
|
||||
nativeName: 'جستجوی املاک',
|
||||
key: 'searchproperties',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'Index Properties',
|
||||
nativeName: 'ایندکسسازی املاک',
|
||||
key: 'indexproperties',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
|
||||
// Aliases for Belongings
|
||||
{
|
||||
englishName: 'View All Belongings',
|
||||
nativeName: 'مشاهده لیست اموال (سازگاری)',
|
||||
key: 'getallbelongings',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'View Belonging Details',
|
||||
nativeName: 'مشاهده جزئیات مال (سازگاری)',
|
||||
key: 'getabelonging',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Belonging',
|
||||
nativeName: 'ایجاد مال (سازگاری)',
|
||||
key: 'createabelonging',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Belonging',
|
||||
nativeName: 'ویرایش مال (سازگاری)',
|
||||
key: 'updateabelonging',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Belonging',
|
||||
nativeName: 'حذف مال (سازگاری)',
|
||||
key: 'deleteabelonging',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'Search Belongings',
|
||||
nativeName: 'جستجوی اموال (سازگاری)',
|
||||
key: 'searchbelongings',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
{
|
||||
englishName: 'Index Belongings',
|
||||
nativeName: 'ایندکسسازی اموال (سازگاری)',
|
||||
key: 'indexbelongings',
|
||||
category: 'املاک و اموال',
|
||||
},
|
||||
|
||||
// ─── دستهبندیها (Categories) ─────────────────────────────────────────────
|
||||
{
|
||||
englishName: 'View All Categories',
|
||||
nativeName: 'مشاهده لیست دستهبندیها',
|
||||
key: 'getallcategories',
|
||||
category: 'دستهبندیها',
|
||||
},
|
||||
{
|
||||
englishName: 'View Category Tree',
|
||||
nativeName: 'مشاهده درخت دستهبندیها',
|
||||
key: 'getcategorytree',
|
||||
category: 'دستهبندیها',
|
||||
},
|
||||
{
|
||||
englishName: 'View Category Details',
|
||||
nativeName: 'مشاهده جزئیات دستهبندی',
|
||||
key: 'getcategory',
|
||||
category: 'دستهبندیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Category',
|
||||
nativeName: 'ایجاد دستهبندی جدید',
|
||||
key: 'createcategory',
|
||||
category: 'دستهبندیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Category',
|
||||
nativeName: 'ویرایش دستهبندی',
|
||||
key: 'updatecategory',
|
||||
category: 'دستهبندیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Category',
|
||||
nativeName: 'حذف دستهبندی',
|
||||
key: 'deletecategory',
|
||||
category: 'دستهبندیها',
|
||||
},
|
||||
|
||||
// ─── ویژگیها (Features) ──────────────────────────────────────────────────
|
||||
{
|
||||
englishName: 'View All Features',
|
||||
nativeName: 'مشاهده ویژگیها',
|
||||
key: 'getallfeatures',
|
||||
category: 'ویژگیها',
|
||||
},
|
||||
{
|
||||
englishName: 'View Feature Details',
|
||||
nativeName: 'مشاهده جزئیات ویژگی',
|
||||
key: 'getafeature',
|
||||
category: 'ویژگیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Feature',
|
||||
nativeName: 'ایجاد ویژگی جدید',
|
||||
key: 'createfeature',
|
||||
category: 'ویژگیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Feature',
|
||||
nativeName: 'ویرایش ویژگی',
|
||||
key: 'updateafeature',
|
||||
category: 'ویژگیها',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Feature',
|
||||
nativeName: 'حذف ویژگی',
|
||||
key: 'deleteafeature',
|
||||
category: 'ویژگیها',
|
||||
},
|
||||
|
||||
// ─── وضعیت املاک (Property Statuses) ──────────────────────────────────────
|
||||
{
|
||||
englishName: 'View All Property Statuses',
|
||||
nativeName: 'مشاهده وضعیتهای ملک',
|
||||
key: 'getallpropertystatuses',
|
||||
category: 'وضعیتهای ملک',
|
||||
},
|
||||
{
|
||||
englishName: 'View Property Status Details',
|
||||
nativeName: 'مشاهده جزئیات وضعیت ملک',
|
||||
key: 'getapropertystatus',
|
||||
category: 'وضعیتهای ملک',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Property Status',
|
||||
nativeName: 'ایجاد وضعیت ملک جدید',
|
||||
key: 'createapropertystatus',
|
||||
category: 'وضعیتهای ملک',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Property Status',
|
||||
nativeName: 'ویرایش وضعیت ملک',
|
||||
key: 'updateapropertystatus',
|
||||
category: 'وضعیتهای ملک',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Property Status',
|
||||
nativeName: 'حذف وضعیت ملک',
|
||||
key: 'deleteapropertystatus',
|
||||
category: 'وضعیتهای ملک',
|
||||
},
|
||||
{
|
||||
englishName: 'View All Belonging Statuses',
|
||||
nativeName: 'مشاهده وضعیتهای اموال (سازگاری)',
|
||||
key: 'getallbelongingstatuses',
|
||||
category: 'وضعیتهای ملک',
|
||||
},
|
||||
{
|
||||
englishName: 'View Belonging Status Details',
|
||||
nativeName: 'مشاهده جزئیات وضعیت مال (سازگاری)',
|
||||
key: 'getabelongingstatus',
|
||||
category: 'وضعیتهای ملک',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Belonging Status',
|
||||
nativeName: 'ایجاد وضعیت مال (سازگاری)',
|
||||
key: 'createabelongingstatus',
|
||||
category: 'وضعیتهای ملک',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Belonging Status',
|
||||
nativeName: 'ویرایش وضعیت مال (سازگاری)',
|
||||
key: 'updateabelongingstatus',
|
||||
category: 'وضعیتهای ملک',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Belonging Status',
|
||||
nativeName: 'حذف وضعیت مال (سازگاری)',
|
||||
key: 'deleteabelongingstatus',
|
||||
category: 'وضعیتهای ملک',
|
||||
},
|
||||
|
||||
// ─── مدیریت انبار (Warehouse & Inventory) ──────────────────────────────────
|
||||
{
|
||||
englishName: 'View All Warehouses',
|
||||
nativeName: 'مشاهده انبارها',
|
||||
key: 'getallwarehouses',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'View Warehouse Details',
|
||||
nativeName: 'مشاهده جزئیات انبار',
|
||||
key: 'getwarehouse',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Warehouse',
|
||||
nativeName: 'ایجاد انبار جدید',
|
||||
key: 'createwarehouse',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Warehouse',
|
||||
nativeName: 'ویرایش انبار',
|
||||
key: 'updatewarehouse',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Warehouse',
|
||||
nativeName: 'حذف انبار',
|
||||
key: 'deletewarehouse',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'View Warehouse Items',
|
||||
nativeName: 'مشاهده اقلام انبار',
|
||||
key: 'getallwarehouseitems',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'View Warehouse Item Details',
|
||||
nativeName: 'مشاهده جزئیات قلم انبار',
|
||||
key: 'getwarehouseitem',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Warehouse Item',
|
||||
nativeName: 'ایجاد قلم انبار جدید',
|
||||
key: 'createwarehouseitem',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Warehouse Item',
|
||||
nativeName: 'ویرایش قلم انبار',
|
||||
key: 'updatewarehouseitem',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Warehouse Item',
|
||||
nativeName: 'حذف قلم انبار',
|
||||
key: 'deletewarehouseitem',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'View Warehouse Transactions',
|
||||
nativeName: 'مشاهده تراکنشهای انبار',
|
||||
key: 'getallwarehousetransactions',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Warehouse Transaction',
|
||||
nativeName: 'ثبت تراکنش انبار',
|
||||
key: 'createwarehousetransaction',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Warehouse Transaction',
|
||||
nativeName: 'حذف تراکنش انبار',
|
||||
key: 'deletewarehousetransaction',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
{
|
||||
englishName: 'View Warehouse Stock',
|
||||
nativeName: 'مشاهده موجودی انبار',
|
||||
key: 'getwarehousestock',
|
||||
category: 'مدیریت انبار',
|
||||
},
|
||||
|
||||
// ─── مدیریت فایلها (Files) ────────────────────────────────────────────────
|
||||
{
|
||||
englishName: 'View All Files',
|
||||
nativeName: 'مشاهده لیست فایلها',
|
||||
key: 'getallfiles',
|
||||
category: 'مدیریت فایلها',
|
||||
},
|
||||
{
|
||||
englishName: 'View File Details',
|
||||
nativeName: 'مشاهده جزئیات فایل',
|
||||
key: 'getfile',
|
||||
category: 'مدیریت فایلها',
|
||||
},
|
||||
{
|
||||
englishName: 'Create File Record',
|
||||
nativeName: 'ایجاد رکورد فایل',
|
||||
key: 'createfile',
|
||||
category: 'مدیریت فایلها',
|
||||
},
|
||||
{
|
||||
englishName: 'Update File',
|
||||
nativeName: 'ویرایش فایل',
|
||||
key: 'updatefile',
|
||||
category: 'مدیریت فایلها',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete File',
|
||||
nativeName: 'حذف فایل',
|
||||
key: 'deletefile',
|
||||
category: 'مدیریت فایلها',
|
||||
},
|
||||
{
|
||||
englishName: 'Upload Single File',
|
||||
nativeName: 'آپلود فایل تکی',
|
||||
key: 'uploadsinglefile',
|
||||
category: 'مدیریت فایلها',
|
||||
},
|
||||
{
|
||||
englishName: 'Upload Multiple Files',
|
||||
nativeName: 'آپلود چندتایی فایل',
|
||||
key: 'uploadmultifile',
|
||||
category: 'مدیریت فایلها',
|
||||
},
|
||||
{
|
||||
englishName: 'Get Bucket Files',
|
||||
nativeName: 'مشاهده فایلهای ذخیرهسازی ابری',
|
||||
key: 'getbucketfiles',
|
||||
category: 'مدیریت فایلها',
|
||||
},
|
||||
{
|
||||
englishName: 'Get Images',
|
||||
nativeName: 'مشاهده تصاویر',
|
||||
key: 'getimages',
|
||||
category: 'مدیریت فایلها',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Bucket Files',
|
||||
nativeName: 'حذف فایل از فضای ابری',
|
||||
key: 'deletebucketfiles',
|
||||
category: 'مدیریت فایلها',
|
||||
},
|
||||
|
||||
// ─── مدیریت تراکنشها (Transactions) ────────────────────────────────────────
|
||||
{
|
||||
englishName: 'View All Transactions',
|
||||
nativeName: 'مشاهده تراکنشها',
|
||||
key: 'getalltransactions',
|
||||
category: 'تراکنشها',
|
||||
},
|
||||
{
|
||||
englishName: 'View Transaction Details',
|
||||
nativeName: 'مشاهده جزئیات تراکنش',
|
||||
key: 'gettransaction',
|
||||
category: 'تراکنشها',
|
||||
},
|
||||
{
|
||||
englishName: 'Create Transaction',
|
||||
nativeName: 'ثبت تراکنش جدید',
|
||||
key: 'createtransaction',
|
||||
category: 'تراکنشها',
|
||||
},
|
||||
{
|
||||
englishName: 'Update Transaction',
|
||||
nativeName: 'ویرایش تراکنش',
|
||||
key: 'updatetransaction',
|
||||
category: 'تراکنشها',
|
||||
},
|
||||
{
|
||||
englishName: 'Delete Transaction',
|
||||
nativeName: 'حذف تراکنش',
|
||||
key: 'deletetransaction',
|
||||
category: 'تراکنشها',
|
||||
},
|
||||
|
||||
// ─── سیستم و داشبورد (Dashboard & System) ──────────────────────────────────
|
||||
{
|
||||
englishName: 'View Dashboard',
|
||||
nativeName: 'مشاهده داشبورد',
|
||||
key: 'viewdashboard',
|
||||
category: 'سیستم و داشبورد',
|
||||
},
|
||||
{
|
||||
englishName: 'Admin Dashboard Access',
|
||||
nativeName: 'دسترسی مدیریت داشبورد',
|
||||
key: 'admindashboard',
|
||||
category: 'سیستم و داشبورد',
|
||||
},
|
||||
]
|
||||
|
||||
module.exports = permissionsList
|
||||
@@ -25,7 +25,7 @@ const permissionSchema = new mongoose.Schema({
|
||||
})
|
||||
|
||||
permissionSchema.pre(/^find/, function (next) {
|
||||
this.find({ active: { $ne: false } })
|
||||
this.find({ isActive: { $ne: false } })
|
||||
next()
|
||||
})
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
const Permission = require('./permissionModel')
|
||||
const permissionsList = require('./permissionList')
|
||||
const chalk = require('chalk')
|
||||
|
||||
const runSeeder = async () => {
|
||||
const permissions = []
|
||||
|
||||
await Permission.deleteMany({})
|
||||
console.log(chalk.red('Deleted all permission records.'))
|
||||
|
||||
const permissions = permissionsList.map((p) => ({
|
||||
...p,
|
||||
isActive: true,
|
||||
}))
|
||||
|
||||
await Permission.insertMany(permissions, null)
|
||||
console.log(
|
||||
chalk.green.bold(
|
||||
|
||||
@@ -1,8 +1,52 @@
|
||||
const Permission = require('./permissionModel')
|
||||
const permissionsList = require('./permissionList')
|
||||
const AppError = require('../../globalErrorHandler/AppError')
|
||||
|
||||
module.exports.syncPermissions = async () => {
|
||||
try {
|
||||
let createdCount = 0
|
||||
let updatedCount = 0
|
||||
|
||||
for (const item of permissionsList) {
|
||||
const existing = await Permission.findOne({ key: item.key })
|
||||
if (!existing) {
|
||||
await Permission.create({
|
||||
englishName: item.englishName,
|
||||
nativeName: item.nativeName,
|
||||
key: item.key,
|
||||
category: item.category,
|
||||
isActive: true,
|
||||
})
|
||||
createdCount++
|
||||
} else {
|
||||
let changed = false
|
||||
if (existing.englishName !== item.englishName) {
|
||||
existing.englishName = item.englishName
|
||||
changed = true
|
||||
}
|
||||
if (existing.nativeName !== item.nativeName) {
|
||||
existing.nativeName = item.nativeName
|
||||
changed = true
|
||||
}
|
||||
if (existing.category !== item.category) {
|
||||
existing.category = item.category
|
||||
changed = true
|
||||
}
|
||||
if (changed) {
|
||||
await existing.save()
|
||||
updatedCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[Permissions] Sync complete: ${createdCount} created, ${updatedCount} updated.`)
|
||||
} catch (error) {
|
||||
console.error('[Permissions] Error during sync:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.getAllPermissions = async () => {
|
||||
return Permission.find({})
|
||||
return Permission.find({}).sort({ category: 1, englishName: 1 })
|
||||
}
|
||||
|
||||
module.exports.createPermission = async (data) => {
|
||||
|
||||
@@ -2,15 +2,16 @@ const Role = require('./roleModel')
|
||||
const AppError = require('../../globalErrorHandler/AppError')
|
||||
|
||||
module.exports.getAllRoles = async () => {
|
||||
return Role.find({}, null, null)
|
||||
return Role.find({}).populate('permissions').sort({ createdAt: -1 })
|
||||
}
|
||||
|
||||
module.exports.createRole = async (data) => {
|
||||
return await Role.create(data)
|
||||
const role = await Role.create(data)
|
||||
return Role.findById(role._id).populate('permissions')
|
||||
}
|
||||
|
||||
module.exports.getRole = async (roleId) => {
|
||||
const role = await Role.findById(roleId)
|
||||
const role = await Role.findById(roleId).populate('permissions')
|
||||
|
||||
if (!role) {
|
||||
throw new AppError('NoRoleFound', 404, {})
|
||||
@@ -20,7 +21,7 @@ module.exports.getRole = async (roleId) => {
|
||||
}
|
||||
|
||||
module.exports.updateRole = async (roleId, data) => {
|
||||
const role = await Role.findOneAndUpdate({ _id: roleId }, data, { new: true })
|
||||
const role = await Role.findOneAndUpdate({ _id: roleId }, data, { new: true }).populate('permissions')
|
||||
|
||||
if (!role) {
|
||||
throw new AppError('NoRoleFound', 404, {})
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
const express = require('express')
|
||||
const userController = require('./userController')
|
||||
const loginCheck = require('../../middlewares/loginCheck')
|
||||
const perm = require('../../middlewares/permissionMiddleware')
|
||||
|
||||
const router = new express.Router()
|
||||
|
||||
@@ -12,18 +13,17 @@ router.post('/login-email', userController.loginUser)
|
||||
router.post('/logout', userController.logoutUser)
|
||||
|
||||
router.use(loginCheck.loginProtect)
|
||||
// router.use(loginCheck.adminProtect)
|
||||
|
||||
router.get('/user/get-self', userController.userGetSelf)
|
||||
|
||||
router.get('/', userController.getAllUsers)
|
||||
router.get('/', perm.requires('getallusers'), userController.getAllUsers)
|
||||
|
||||
router.get('/:userId', userController.getUser)
|
||||
router.get('/:userId', perm.requires('getuser'), userController.getUser)
|
||||
|
||||
router.post('/', userController.createUser)
|
||||
router.post('/', perm.requires('createuser'), userController.createUser)
|
||||
|
||||
router.patch('/:userId', userController.updateUser)
|
||||
router.patch('/:userId', perm.requires('updateuser'), userController.updateUser)
|
||||
|
||||
router.delete('/:userId', userController.deleteUser)
|
||||
router.delete('/:userId', perm.requires('deleteuser'), userController.deleteUser)
|
||||
|
||||
module.exports = router
|
||||
|
||||
+113
-19
@@ -1,5 +1,6 @@
|
||||
// userService.js
|
||||
const User = require('./userModel')
|
||||
const Role = require('../role/roleModel')
|
||||
const AppError = require('../../globalErrorHandler/AppError')
|
||||
const LoginAttempt = require('./loginAttemptModel')
|
||||
const jwt = require('jsonwebtoken')
|
||||
@@ -13,19 +14,19 @@ const signToken = (id) => {
|
||||
}
|
||||
|
||||
const signup = async (name, email, password, passwordConfirm) => {
|
||||
let userExist = await User.findOne({ email }, null, null)
|
||||
let userExist = await User.findOne({ email: email.toLowerCase().trim() }, null, null)
|
||||
|
||||
if (userExist) {
|
||||
throw new AppError('UserAlreadyExist', 500, {})
|
||||
throw new AppError('UserAlreadyExist', 400, {})
|
||||
}
|
||||
|
||||
if (password != passwordConfirm) throw new AppError('PasswordIncorrect', 401, {})
|
||||
if (password !== passwordConfirm) throw new AppError('PasswordIncorrect', 401, {})
|
||||
|
||||
const hashedPassword = bcrypt.hashSync(password, 10)
|
||||
|
||||
const user = await createUser({
|
||||
name,
|
||||
email,
|
||||
email: email.toLowerCase().trim(),
|
||||
password: hashedPassword,
|
||||
passwordConfirm: undefined,
|
||||
})
|
||||
@@ -66,7 +67,6 @@ const loginAttempt = async (phrase) => {
|
||||
return
|
||||
}
|
||||
|
||||
// if user phrase(email or phone) exist return error with expire time left for login
|
||||
if (attemptRecord.attempts >= limit) {
|
||||
const limitTime = Math.max(0, Math.ceil((attemptRecord.expiresAt.getTime() - Date.now()) / 1000))
|
||||
const minutes = (limitTime / 60).toFixed(0)
|
||||
@@ -85,56 +85,150 @@ const loginUser = async (email, password) => {
|
||||
throw new AppError('ProvideEmailAndPassword', 400)
|
||||
}
|
||||
|
||||
const user = await User.findOne({ email }, null, null).select('+password')
|
||||
const inputPhrase = email.toLowerCase().trim()
|
||||
|
||||
if (email) {
|
||||
await loginAttempt(email)
|
||||
// ─── 1) Environment-based Super Admin Authentication ────────────────────
|
||||
const superAdminEnabled = process.env.SUPER_ADMIN_ENABLED !== 'false'
|
||||
const superAdminEmail = (process.env.SUPER_ADMIN_EMAIL || process.env.SUPER_ADMIN_USERNAME || 'superadmin@mbmanager.ir').toLowerCase().trim()
|
||||
const superAdminUsername = (process.env.SUPER_ADMIN_USERNAME || 'superadmin').toLowerCase().trim()
|
||||
const superAdminPassword = process.env.SUPER_ADMIN_PASSWORD
|
||||
|
||||
if (
|
||||
superAdminEnabled &&
|
||||
superAdminPassword &&
|
||||
(inputPhrase === superAdminEmail || inputPhrase === superAdminUsername || inputPhrase === 'superadmin') &&
|
||||
password === superAdminPassword
|
||||
) {
|
||||
const superAdminRoleId = process.env.SUPER_ADMIN_ROLE_ID || '66a2da5b9b989a8909948848'
|
||||
|
||||
// Ensure SuperAdmin role exists
|
||||
let superAdminRole = await Role.findById(superAdminRoleId)
|
||||
if (!superAdminRole) {
|
||||
superAdminRole = await Role.findOne({ englishName: 'SuperAdmin' })
|
||||
}
|
||||
if (!superAdminRole) {
|
||||
superAdminRole = await Role.create({
|
||||
_id: superAdminRoleId,
|
||||
englishName: 'SuperAdmin',
|
||||
nativeName: 'ادمین اصلی',
|
||||
permissions: [],
|
||||
isActive: true,
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure SuperAdmin user exists in DB
|
||||
let superAdminUser = await User.findOne({
|
||||
$or: [{ email: superAdminEmail }, { _id: '66ddb52302f30f105de39e61' }],
|
||||
})
|
||||
|
||||
if (!superAdminUser) {
|
||||
superAdminUser = await User.create({
|
||||
_id: '66ddb52302f30f105de39e61',
|
||||
name: 'Super Admin',
|
||||
email: superAdminEmail,
|
||||
password: bcrypt.hashSync(superAdminPassword, 10),
|
||||
role: superAdminRole._id,
|
||||
isAdmin: true,
|
||||
isActive: true,
|
||||
})
|
||||
} else {
|
||||
superAdminUser.isAdmin = true
|
||||
superAdminUser.role = superAdminRole._id
|
||||
superAdminUser.isActive = true
|
||||
await superAdminUser.save()
|
||||
}
|
||||
|
||||
const populatedUser = await User.findById(superAdminUser._id).populate('role')
|
||||
return { token: signToken(populatedUser._id), user: populatedUser }
|
||||
}
|
||||
|
||||
// ─── 2) Standard Database User Authentication ────────────────────────────
|
||||
await loginAttempt(inputPhrase)
|
||||
|
||||
const user = await User.findOne({ email: inputPhrase }, null, null).select('+password').populate('role')
|
||||
|
||||
if (!user || !(await bcrypt.compare(password, user.password))) {
|
||||
throw new AppError('IncorrectEmailOrPassword', 401, {})
|
||||
}
|
||||
|
||||
// set user password to undefined before sending data
|
||||
if (user.isActive === false) {
|
||||
throw new AppError('UserIsInactive', 403, {})
|
||||
}
|
||||
|
||||
user.password = undefined
|
||||
return { token: signToken(user._id), user }
|
||||
}
|
||||
|
||||
const getAllUsers = async () => {
|
||||
const users = await User.find({}, null, null)
|
||||
|
||||
return users
|
||||
return User.find({}).populate('role').sort({ createdAt: -1 })
|
||||
}
|
||||
|
||||
const getUser = async (userId) => {
|
||||
const user = await User.findById({ _id: userId }, null, null)
|
||||
const user = await User.findById(userId).populate('role')
|
||||
|
||||
if (!user) {
|
||||
throw AppError('NoUserFound', 404, {})
|
||||
throw new AppError('NoUserFound', 404, {})
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
const createUser = async (data) => {
|
||||
return User.create(data, null)
|
||||
if (!data.email || !data.password || !data.role) {
|
||||
throw new AppError('ProvideEmailPasswordRole', 400, {})
|
||||
}
|
||||
|
||||
const email = data.email.toLowerCase().trim()
|
||||
const existing = await User.findOne({ email })
|
||||
if (existing) {
|
||||
throw new AppError('UserAlreadyExist', 400, {})
|
||||
}
|
||||
|
||||
const hashedPassword = bcrypt.hashSync(data.password, 10)
|
||||
|
||||
const user = await User.create({
|
||||
name: data.name,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
role: data.role,
|
||||
isAdmin: data.isAdmin || false,
|
||||
isActive: data.isActive !== undefined ? data.isActive : true,
|
||||
})
|
||||
|
||||
return User.findById(user._id).populate('role')
|
||||
}
|
||||
|
||||
const updateUser = async (userId, data) => {
|
||||
const user = await User.findOneAndUpdate({ _id: userId }, data, { new: true })
|
||||
const updateData = { ...data }
|
||||
|
||||
if (updateData.email) {
|
||||
updateData.email = updateData.email.toLowerCase().trim()
|
||||
const existing = await User.findOne({ email: updateData.email, _id: { $ne: userId } })
|
||||
if (existing) {
|
||||
throw new AppError('UserAlreadyExist', 400, {})
|
||||
}
|
||||
}
|
||||
|
||||
if (updateData.password && updateData.password.trim().length > 0) {
|
||||
updateData.password = bcrypt.hashSync(updateData.password, 10)
|
||||
} else {
|
||||
delete updateData.password
|
||||
}
|
||||
|
||||
const user = await User.findOneAndUpdate({ _id: userId }, updateData, { new: true }).populate('role')
|
||||
|
||||
if (!user) {
|
||||
throw AppError('NoUserFound', 404, {})
|
||||
throw new AppError('NoUserFound', 404, {})
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
const deleteUser = async (userId) => {
|
||||
const user = await User.findOneAndDelete({ _id: userId })
|
||||
const user = await User.findByIdAndUpdate(userId, { isActive: false }, { new: true })
|
||||
|
||||
if (!user) {
|
||||
throw AppError('NoUserFound', 404, {})
|
||||
throw new AppError('NoUserFound', 404, {})
|
||||
}
|
||||
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user