Merge pull request 'hasan_backend' (#9) from hasan_backend into dev
Reviewed-on: https://gitea.maskantech.in/gitea_admin/productcatalogue_backend/pulls/9
This commit is contained in:
@@ -19,7 +19,7 @@ export default (sequelize) => {
|
||||
allowNull: false
|
||||
},
|
||||
changed_by: {
|
||||
type: DataTypes.UUID,
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
changes: {
|
||||
|
||||
@@ -19,7 +19,7 @@ export default (sequelize) => {
|
||||
allowNull: false
|
||||
},
|
||||
changed_by: {
|
||||
type: DataTypes.UUID,
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
changes: {
|
||||
|
||||
@@ -5,13 +5,13 @@ function serializeAttribute(attr) {
|
||||
const json = attr.toJSON ? attr.toJSON() : { ...attr };
|
||||
|
||||
// Mapping to camelCase
|
||||
json.isRequired = json.is_required ?? false;
|
||||
json.isUnique = json.is_unique ?? false;
|
||||
json.isLocalizable = json.is_localizable ?? false;
|
||||
json.isVariantEligible = json.is_variant_eligible ?? false;
|
||||
json.isSearchable = json.is_searchable ?? false;
|
||||
json.isFilterable = json.is_filterable ?? false;
|
||||
json.isChannelSpecific = json.is_channel_specific ?? false;
|
||||
json.isRequired = json.is_required ?? json.isRequired ?? false;
|
||||
json.isUnique = json.is_unique ?? json.isUnique ?? false;
|
||||
json.isLocalizable = json.is_localizable ?? json.isLocalizable ?? false;
|
||||
json.isVariantEligible = json.is_variant_eligible ?? json.isVariantEligible ?? false;
|
||||
json.isSearchable = json.is_searchable ?? json.isSearchable ?? false;
|
||||
json.isFilterable = json.is_filterable ?? json.isFilterable ?? false;
|
||||
json.isChannelSpecific = json.is_channel_specific ?? json.isChannelSpecific ?? false;
|
||||
json.minLength = json.min_length ?? 0;
|
||||
json.maxLength = json.max_length ?? 255;
|
||||
json.regexPattern = json.regex_pattern ?? null;
|
||||
|
||||
@@ -119,15 +119,15 @@ export default (sequelize) => {
|
||||
defaultValue: 0
|
||||
},
|
||||
created_by: {
|
||||
type: DataTypes.UUID,
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
updated_by: {
|
||||
type: DataTypes.UUID,
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
deleted_by: {
|
||||
type: DataTypes.UUID,
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
help_text: {
|
||||
|
||||
@@ -19,7 +19,7 @@ export default (sequelize) => {
|
||||
allowNull: false
|
||||
},
|
||||
changed_by: {
|
||||
type: DataTypes.UUID,
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
changes: {
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
import { Model, DataTypes } from 'sequelize';
|
||||
|
||||
export class Role extends Model {
|
||||
static associate(models) {}
|
||||
static associate(models) { }
|
||||
}
|
||||
|
||||
export default (sequelize) => {
|
||||
Role.init({
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
tenant_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true
|
||||
},
|
||||
role_code: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false
|
||||
},
|
||||
role_name: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT
|
||||
type: DataTypes.TEXT
|
||||
},
|
||||
role_type: {
|
||||
type: DataTypes.STRING(20),
|
||||
defaultValue: 'tenant',
|
||||
allowNull: false
|
||||
type: DataTypes.STRING(20),
|
||||
defaultValue: 'tenant',
|
||||
allowNull: false
|
||||
},
|
||||
is_system_role: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true
|
||||
},
|
||||
created_by: {
|
||||
type: DataTypes.INTEGER
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
updated_by: {
|
||||
type: DataTypes.INTEGER
|
||||
type: DataTypes.INTEGER
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
|
||||
@@ -7,6 +7,13 @@ export class RoleRepository {
|
||||
const tenantFilter = context.userType !== 'platform' && context.tenantId
|
||||
? { tenant_id: context.tenantId }
|
||||
: {};
|
||||
|
||||
const { where: optionsWhere, ...restOptions } = options;
|
||||
const whereClause = {
|
||||
...(optionsWhere || {}),
|
||||
...tenantFilter
|
||||
};
|
||||
|
||||
return await models.Role.findAll({
|
||||
include: [
|
||||
{
|
||||
@@ -15,11 +22,8 @@ export class RoleRepository {
|
||||
through: { attributes: ['can_view', 'can_create', 'can_edit', 'can_delete', 'can_alter', 'can_import', 'can_export'] }
|
||||
}
|
||||
],
|
||||
...options,
|
||||
where: {
|
||||
...(options.where || {}),
|
||||
...tenantFilter
|
||||
}
|
||||
...restOptions,
|
||||
where: whereClause
|
||||
});
|
||||
}
|
||||
|
||||
@@ -48,7 +52,7 @@ export class RoleRepository {
|
||||
roleData.tenant_id = context.tenantId;
|
||||
}
|
||||
const role = await models.Role.create(roleData, { transaction });
|
||||
|
||||
|
||||
if (permissions && permissions.length > 0) {
|
||||
const validPermissions = permissions.filter(p => p.node_id != null);
|
||||
if (validPermissions.length > 0) {
|
||||
@@ -66,7 +70,7 @@ export class RoleRepository {
|
||||
await models.RolePermission.bulkCreate(rolePermissions, { transaction });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await transaction.commit();
|
||||
return await this.findById(role.id, {}, context);
|
||||
} catch (error) {
|
||||
@@ -137,9 +141,9 @@ export class RoleRepository {
|
||||
// Delete associations first
|
||||
await models.RolePermission.destroy({ where: { role_id: id }, transaction });
|
||||
await models.UserRole.destroy({ where: { role_id: id }, transaction });
|
||||
|
||||
|
||||
await role.destroy({ transaction });
|
||||
|
||||
|
||||
await transaction.commit();
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import repository from './role.repository.js';
|
||||
import { models } from '../../../shared/database/models.js';
|
||||
import { ApiError } from '../../../utils/helpers/ApiError.utils.js';
|
||||
import { Op } from 'sequelize';
|
||||
|
||||
export class RoleService {
|
||||
async getAll(query = {}, context = {}) {
|
||||
@@ -8,13 +9,17 @@ export class RoleService {
|
||||
}
|
||||
|
||||
async getByTenant(tenantId, context = {}) {
|
||||
if (!tenantId || tenantId === 'platform' || tenantId === 'null' || tenantId === 'undefined') {
|
||||
return await repository.findAll({ where: { tenant_id: null } }, context);
|
||||
}
|
||||
if (tenantId === 'all') {
|
||||
return await repository.findAll({}, context);
|
||||
}
|
||||
const numericId = isNaN(Number(tenantId)) ? tenantId : Number(tenantId);
|
||||
return await repository.findAll({
|
||||
where: tenantId
|
||||
? { tenant_id: tenantId }
|
||||
: { tenant_id: null }
|
||||
where: {
|
||||
tenant_id: numericId
|
||||
}
|
||||
}, context);
|
||||
}
|
||||
|
||||
@@ -60,6 +65,15 @@ export class RoleService {
|
||||
}
|
||||
|
||||
async delete(id, context = {}) {
|
||||
const role = await repository.findById(id, {}, context);
|
||||
if (!role) {
|
||||
throw new ApiError(404, 'Role not found');
|
||||
}
|
||||
|
||||
if (role.is_system_role || role.role_code === 'SUPER_ADMIN' || role.role_code === 'SUPERADMIN' || role.role_name?.toLowerCase().includes('super admin')) {
|
||||
throw new ApiError(403, 'Forbidden: Super Admin and System roles cannot be deleted');
|
||||
}
|
||||
|
||||
const deleted = await repository.delete(id, context);
|
||||
if (!deleted) {
|
||||
throw new ApiError(404, 'Role not found');
|
||||
|
||||
@@ -82,6 +82,22 @@ export class UserService {
|
||||
}
|
||||
|
||||
async delete(id, context = {}) {
|
||||
const user = await repository.findById(id, {}, context);
|
||||
if (!user) {
|
||||
throw new ApiError(404, 'User not found');
|
||||
}
|
||||
|
||||
const isSuperAdmin = user.email === 'admin@admin.com' || (user.roles && user.roles.some(r =>
|
||||
r.role_code === 'SUPER_ADMIN' ||
|
||||
r.role_code === 'SUPERADMIN' ||
|
||||
r.role_name?.toLowerCase().includes('super admin') ||
|
||||
r.is_system_role
|
||||
));
|
||||
|
||||
if (isSuperAdmin) {
|
||||
throw new ApiError(403, 'Forbidden: Super Admin user cannot be deleted');
|
||||
}
|
||||
|
||||
const deleted = await repository.delete(id, context);
|
||||
if (!deleted) {
|
||||
throw new ApiError(404, 'User not found');
|
||||
|
||||
@@ -4,11 +4,11 @@ import { authenticate } from '../../../shared/middleware/auth.middleware.js';
|
||||
import { validate } from '../../../shared/middleware/validation.middleware.js';
|
||||
import { authorize } from '../../../shared/middleware/permission.middleware.js';
|
||||
import { audit } from '../../../shared/middleware/audit.middleware.js';
|
||||
import {
|
||||
createValidation,
|
||||
updateValidation,
|
||||
deleteValidation,
|
||||
getByIdValidation
|
||||
import {
|
||||
createValidation,
|
||||
updateValidation,
|
||||
deleteValidation,
|
||||
getByIdValidation
|
||||
} from './metric.validation.js';
|
||||
|
||||
const router = Router();
|
||||
@@ -26,7 +26,7 @@ const router = Router();
|
||||
router.get(
|
||||
'/',
|
||||
authenticate,
|
||||
authorize(['reports.sales']),
|
||||
authorize(['reports']),
|
||||
controller.getAll
|
||||
);
|
||||
|
||||
@@ -45,11 +45,13 @@ router.get(
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Success
|
||||
* 404:
|
||||
* description: Not Found
|
||||
*/
|
||||
router.get(
|
||||
'/:id',
|
||||
authenticate,
|
||||
authorize(['reports.sales']),
|
||||
authorize(['reports']),
|
||||
getByIdValidation,
|
||||
validate,
|
||||
controller.getById
|
||||
@@ -68,7 +70,7 @@ router.get(
|
||||
router.post(
|
||||
'/',
|
||||
authenticate,
|
||||
authorize(['reports.sales']),
|
||||
authorize(['reports']),
|
||||
createValidation,
|
||||
validate,
|
||||
audit('CREATE_METRIC'),
|
||||
@@ -92,7 +94,7 @@ router.post(
|
||||
router.put(
|
||||
'/:id',
|
||||
authenticate,
|
||||
authorize(['reports.sales']),
|
||||
authorize(['reports']),
|
||||
updateValidation,
|
||||
validate,
|
||||
audit('UPDATE_METRIC'),
|
||||
@@ -116,7 +118,7 @@ router.put(
|
||||
router.delete(
|
||||
'/:id',
|
||||
authenticate,
|
||||
authorize(['reports.sales']),
|
||||
authorize(['reports']),
|
||||
deleteValidation,
|
||||
validate,
|
||||
audit('DELETE_METRIC'),
|
||||
|
||||
@@ -25,7 +25,7 @@ router.get(
|
||||
router.patch(
|
||||
'/read-all',
|
||||
authenticate,
|
||||
authorize(['notifications.update']),
|
||||
authorize(['notifications']),
|
||||
controller.markAllRead
|
||||
);
|
||||
|
||||
@@ -43,7 +43,7 @@ router.get(
|
||||
router.patch(
|
||||
'/:id/read',
|
||||
authenticate,
|
||||
authorize(['notifications.update']),
|
||||
authorize(['notifications']),
|
||||
markReadValidation,
|
||||
validate,
|
||||
controller.markRead
|
||||
|
||||
@@ -26,7 +26,7 @@ const router = Router();
|
||||
router.get(
|
||||
'/',
|
||||
authenticate,
|
||||
authorize(['TENANTS_MANAGEMENT']),
|
||||
authorize(['settings.tenants']),
|
||||
controller.getAll
|
||||
);
|
||||
|
||||
@@ -49,7 +49,7 @@ router.get(
|
||||
router.get(
|
||||
'/:id',
|
||||
authenticate,
|
||||
authorize(['TENANTS_MANAGEMENT']),
|
||||
authorize(['settings.tenants']),
|
||||
getByIdValidation,
|
||||
validate,
|
||||
controller.getById
|
||||
@@ -68,7 +68,7 @@ router.get(
|
||||
router.post(
|
||||
'/',
|
||||
authenticate,
|
||||
authorize(['TENANTS_MANAGEMENT']),
|
||||
authorize(['settings.tenants']),
|
||||
createValidation,
|
||||
validate,
|
||||
audit('CREATE_ORG'),
|
||||
@@ -92,7 +92,7 @@ router.post(
|
||||
router.put(
|
||||
'/:id',
|
||||
authenticate,
|
||||
authorize(['TENANTS_MANAGEMENT']),
|
||||
authorize(['settings.tenants']),
|
||||
updateValidation,
|
||||
validate,
|
||||
audit('UPDATE_ORG'),
|
||||
@@ -116,7 +116,7 @@ router.put(
|
||||
router.delete(
|
||||
'/:id',
|
||||
authenticate,
|
||||
authorize(['TENANTS_MANAGEMENT']),
|
||||
authorize(['settings.tenants']),
|
||||
deleteValidation,
|
||||
validate,
|
||||
audit('DELETE_ORG'),
|
||||
|
||||
@@ -22,7 +22,7 @@ const router = Router();
|
||||
router.get(
|
||||
'/',
|
||||
authenticate,
|
||||
authorize(['TENANTS_MANAGEMENT']),
|
||||
authorize(['settings.tenants']),
|
||||
controller.getAll
|
||||
);
|
||||
|
||||
@@ -49,7 +49,7 @@ router.get(
|
||||
router.get(
|
||||
'/:id',
|
||||
authenticate,
|
||||
authorize(['TENANTS_MANAGEMENT']),
|
||||
authorize(['settings.tenants']),
|
||||
controller.getById
|
||||
);
|
||||
|
||||
@@ -82,7 +82,7 @@ router.get(
|
||||
router.post(
|
||||
'/',
|
||||
authenticate,
|
||||
authorize(['TENANTS_MANAGEMENT']),
|
||||
authorize(['settings.tenants']),
|
||||
controller.create
|
||||
);
|
||||
|
||||
@@ -107,7 +107,7 @@ router.post(
|
||||
router.put(
|
||||
'/:id',
|
||||
authenticate,
|
||||
authorize(['TENANTS_MANAGEMENT']),
|
||||
authorize(['settings.tenants']),
|
||||
controller.update
|
||||
);
|
||||
|
||||
@@ -132,7 +132,7 @@ router.put(
|
||||
router.delete(
|
||||
'/:id',
|
||||
authenticate,
|
||||
authorize(['TENANTS_MANAGEMENT']),
|
||||
authorize(['settings.tenants']),
|
||||
controller.delete
|
||||
);
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ router.get(
|
||||
router.post(
|
||||
'/generate',
|
||||
authenticate,
|
||||
authorize(['write:products']),
|
||||
authorize(['products.variants']),
|
||||
audit('GENERATE_VARIANTS'),
|
||||
controller.generateBatch
|
||||
);
|
||||
@@ -43,7 +43,7 @@ router.post(
|
||||
router.post(
|
||||
'/bulk-update',
|
||||
authenticate,
|
||||
authorize(['write:products']),
|
||||
authorize(['products.variants']),
|
||||
audit('BULK_UPDATE_VARIANTS'),
|
||||
controller.bulkUpdate
|
||||
);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
// 1. Alter attributes table
|
||||
const attributesTable = await queryInterface.describeTable('attributes').catch(() => null);
|
||||
if (attributesTable) {
|
||||
if (attributesTable.created_by) {
|
||||
await queryInterface.removeColumn('attributes', 'created_by');
|
||||
}
|
||||
if (attributesTable.updated_by) {
|
||||
await queryInterface.removeColumn('attributes', 'updated_by');
|
||||
}
|
||||
if (attributesTable.deleted_by) {
|
||||
await queryInterface.removeColumn('attributes', 'deleted_by');
|
||||
}
|
||||
|
||||
await queryInterface.addColumn('attributes', 'created_by', {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true
|
||||
});
|
||||
await queryInterface.addColumn('attributes', 'updated_by', {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true
|
||||
});
|
||||
await queryInterface.addColumn('attributes', 'deleted_by', {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Alter history tables
|
||||
const historyTables = ['attribute_history', 'attribute_set_history', 'attribute_group_history'];
|
||||
for (const table of historyTables) {
|
||||
const tableInfo = await queryInterface.describeTable(table).catch(() => null);
|
||||
if (tableInfo) {
|
||||
if (tableInfo.changed_by) {
|
||||
await queryInterface.removeColumn(table, 'changed_by');
|
||||
}
|
||||
await queryInterface.addColumn(table, 'changed_by', {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
// Revert attributes table
|
||||
const attributesTable = await queryInterface.describeTable('attributes').catch(() => null);
|
||||
if (attributesTable) {
|
||||
if (attributesTable.created_by) {
|
||||
await queryInterface.removeColumn('attributes', 'created_by');
|
||||
}
|
||||
if (attributesTable.updated_by) {
|
||||
await queryInterface.removeColumn('attributes', 'updated_by');
|
||||
}
|
||||
if (attributesTable.deleted_by) {
|
||||
await queryInterface.removeColumn('attributes', 'deleted_by');
|
||||
}
|
||||
|
||||
await queryInterface.addColumn('attributes', 'created_by', {
|
||||
type: Sequelize.UUID,
|
||||
allowNull: true
|
||||
});
|
||||
await queryInterface.addColumn('attributes', 'updated_by', {
|
||||
type: Sequelize.UUID,
|
||||
allowNull: true
|
||||
});
|
||||
await queryInterface.addColumn('attributes', 'deleted_by', {
|
||||
type: Sequelize.UUID,
|
||||
allowNull: true
|
||||
});
|
||||
}
|
||||
|
||||
// Revert history tables
|
||||
const historyTables = ['attribute_history', 'attribute_set_history', 'attribute_group_history'];
|
||||
for (const table of historyTables) {
|
||||
const tableInfo = await queryInterface.describeTable(table).catch(() => null);
|
||||
if (tableInfo) {
|
||||
if (tableInfo.changed_by) {
|
||||
await queryInterface.removeColumn(table, 'changed_by');
|
||||
}
|
||||
await queryInterface.addColumn(table, 'changed_by', {
|
||||
type: Sequelize.UUID,
|
||||
allowNull: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,259 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
const now = new Date();
|
||||
|
||||
// 1. Insert parent groups if not exist
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO permission_nodes (node_code, node_name, node_type, parent_id, node_level, display_order, can_view, can_create, can_edit, can_delete, created_at, updated_at)
|
||||
VALUES
|
||||
('products', 'Products', 'group', NULL, 1, 10, true, false, false, false, NOW(), NOW()),
|
||||
('masters', 'Masters', 'group', NULL, 1, 20, true, false, false, false, NOW(), NOW()),
|
||||
('settings', 'Settings', 'group', NULL, 1, 30, true, false, false, false, NOW(), NOW()),
|
||||
('reports', 'Reports', 'group', NULL, 1, 40, true, false, false, false, NOW(), NOW())
|
||||
ON CONFLICT (node_code) DO NOTHING;
|
||||
`, { transaction });
|
||||
|
||||
// Fetch parent group IDs
|
||||
const parentGroups = await queryInterface.sequelize.query(
|
||||
`SELECT id, node_code FROM permission_nodes WHERE node_code IN ('products', 'masters', 'settings', 'reports')`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
const pid = {};
|
||||
parentGroups.forEach(g => { pid[g.node_code] = g.id; });
|
||||
|
||||
// 2. Insert new permission nodes
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO permission_nodes (node_code, node_name, node_type, parent_id, node_level, display_order, can_view, can_create, can_edit, can_delete, created_at, updated_at)
|
||||
VALUES
|
||||
('settings.tenants', 'Tenants', 'feature', ${pid['settings']}, 2, 35, true, true, true, true, NOW(), NOW()),
|
||||
('notifications', 'Notifications', 'feature', ${pid['settings']}, 2, 34, true, false, true, false, NOW(), NOW())
|
||||
ON CONFLICT (node_code) DO NOTHING;
|
||||
`, { transaction });
|
||||
|
||||
// Fetch settings.tenants and notifications IDs
|
||||
const [tenantsNode] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM permission_nodes WHERE node_code = 'settings.tenants'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
const [notificationsNode] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM permission_nodes WHERE node_code = 'notifications'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
// 3. Map legacy roles assignments from TENANTS_MANAGEMENT to settings.tenants
|
||||
const [legacyTenantsNode] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM permission_nodes WHERE node_code = 'TENANTS_MANAGEMENT'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (legacyTenantsNode && tenantsNode) {
|
||||
const legacyMappings = await queryInterface.sequelize.query(
|
||||
`SELECT role_id, can_view, can_create, can_edit, can_delete FROM role_permissions WHERE node_id = ${legacyTenantsNode.id}`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
for (const mapping of legacyMappings) {
|
||||
// Check if mapping already exists to prevent duplicate insertion
|
||||
const [exists] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM role_permissions WHERE role_id = ${mapping.role_id} AND node_id = ${tenantsNode.id}`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (!exists) {
|
||||
await queryInterface.bulkInsert('role_permissions', [{
|
||||
role_id: mapping.role_id,
|
||||
node_id: tenantsNode.id,
|
||||
can_view: mapping.can_view,
|
||||
can_create: mapping.can_create,
|
||||
can_edit: mapping.can_edit,
|
||||
can_delete: mapping.can_delete,
|
||||
can_alter: false,
|
||||
can_export: false,
|
||||
can_import: false,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}], { transaction });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Default notifications permission mapping (mimic settings.roles permission level)
|
||||
const [rolesNode] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM permission_nodes WHERE node_code = 'settings.roles'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (rolesNode && notificationsNode) {
|
||||
const roleMappings = await queryInterface.sequelize.query(
|
||||
`SELECT role_id, can_view, can_edit FROM role_permissions WHERE node_id = ${rolesNode.id}`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
for (const mapping of roleMappings) {
|
||||
const [exists] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM role_permissions WHERE role_id = ${mapping.role_id} AND node_id = ${notificationsNode.id}`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (!exists) {
|
||||
await queryInterface.bulkInsert('role_permissions', [{
|
||||
role_id: mapping.role_id,
|
||||
node_id: notificationsNode.id,
|
||||
can_view: mapping.can_view,
|
||||
can_create: false,
|
||||
can_edit: mapping.can_edit,
|
||||
can_delete: false,
|
||||
can_alter: false,
|
||||
can_export: false,
|
||||
can_import: false,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}], { transaction });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Move child nodes under proper parent groups
|
||||
const mappings = [
|
||||
{ code: 'products.items', parent: 'products', level: 2, order: 11 },
|
||||
{ code: 'products.variants', parent: 'products', level: 2, order: 12 },
|
||||
{ code: 'products.families', parent: 'products', level: 2, order: 13 },
|
||||
{ code: 'products.categories', parent: 'products', level: 2, order: 14 },
|
||||
{ code: 'products.attributes', parent: 'products', level: 2, order: 15 },
|
||||
{ code: 'masters.brands', parent: 'masters', level: 2, order: 21 },
|
||||
{ code: 'masters.units', parent: 'masters', level: 2, order: 22 },
|
||||
{ code: 'settings.users', parent: 'settings', level: 2, order: 31 },
|
||||
{ code: 'settings.roles', parent: 'settings', level: 2, order: 32 },
|
||||
{ code: 'settings.integrations',parent: 'settings', level: 2, order: 33 },
|
||||
{ code: 'reports.sales', parent: 'reports', level: 2, order: 41 },
|
||||
{ code: 'reports.inventory', parent: 'reports', level: 2, order: 42 }
|
||||
];
|
||||
|
||||
for (const m of mappings) {
|
||||
await queryInterface.sequelize.query(
|
||||
`UPDATE permission_nodes
|
||||
SET parent_id = ${pid[m.parent]}, node_level = ${m.level}, display_order = ${m.order}
|
||||
WHERE node_code = '${m.code}'`,
|
||||
{ transaction }
|
||||
);
|
||||
}
|
||||
|
||||
// 6. Safely Delete Legacy and Unwanted/Obsolete Permission Nodes (PIM_SYSTEM, USERS_MANAGEMENT, ROLES_MANAGEMENT, TENANTS_MANAGEMENT, inventory.stock, inventory.adjustments)
|
||||
// Cascade delete rules will clear all related role_permissions mappings automatically
|
||||
await queryInterface.sequelize.query(
|
||||
`DELETE FROM permission_nodes WHERE node_code IN ('PIM_SYSTEM', 'inventory.stock', 'inventory.adjustments')`,
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
const now = new Date();
|
||||
|
||||
// 1. Re-create Root Permission Node (PIM System)
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO permission_nodes (node_code, node_name, node_type, parent_id, node_level, display_order, can_view, can_create, can_edit, can_delete, created_at, updated_at)
|
||||
VALUES ('PIM_SYSTEM', 'PIM System', 'module', NULL, 1, 1, true, false, false, false, NOW(), NOW())
|
||||
ON CONFLICT (node_code) DO NOTHING;
|
||||
`, { transaction });
|
||||
|
||||
const [pimSystemNode] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM permission_nodes WHERE node_code = 'PIM_SYSTEM'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
// 2. Re-create child nodes (Settings, Users, Roles)
|
||||
const legacyChildren = [
|
||||
{ code: 'USERS_MANAGEMENT', name: 'Users Management' },
|
||||
{ code: 'ROLES_MANAGEMENT', name: 'Roles Management' },
|
||||
{ code: 'TENANTS_MANAGEMENT', name: 'Tenants Management' }
|
||||
];
|
||||
|
||||
for (let i = 0; i < legacyChildren.length; i++) {
|
||||
const child = legacyChildren[i];
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO permission_nodes (node_code, node_name, node_type, parent_id, node_level, display_order, can_view, can_create, can_edit, can_delete, created_at, updated_at)
|
||||
VALUES ('${child.code}', '${child.name}', 'feature', ${pimSystemNode.id}, 2, ${i + 1}, true, true, true, true, NOW(), NOW())
|
||||
ON CONFLICT (node_code) DO NOTHING;
|
||||
`, { transaction });
|
||||
}
|
||||
|
||||
// Fetch TENANTS_MANAGEMENT ID
|
||||
const [legacyTenantsNode] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM permission_nodes WHERE node_code = 'TENANTS_MANAGEMENT'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
// Fetch settings.tenants ID
|
||||
const [tenantsNode] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM permission_nodes WHERE node_code = 'settings.tenants'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
// 3. Rollback role permissions mapping back to TENANTS_MANAGEMENT
|
||||
if (tenantsNode && legacyTenantsNode) {
|
||||
const mappings = await queryInterface.sequelize.query(
|
||||
`SELECT role_id, can_view, can_create, can_edit, can_delete FROM role_permissions WHERE node_id = ${tenantsNode.id}`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
for (const mapping of mappings) {
|
||||
const [exists] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM role_permissions WHERE role_id = ${mapping.role_id} AND node_id = ${legacyTenantsNode.id}`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (!exists) {
|
||||
await queryInterface.bulkInsert('role_permissions', [{
|
||||
role_id: mapping.role_id,
|
||||
node_id: legacyTenantsNode.id,
|
||||
can_view: mapping.can_view,
|
||||
can_create: mapping.can_create,
|
||||
can_edit: mapping.can_edit,
|
||||
can_delete: mapping.can_delete,
|
||||
can_alter: false,
|
||||
can_export: false,
|
||||
can_import: false,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}], { transaction });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Decouple child nodes from parent groups BEFORE deleting parent groups to avoid cascading delete lockout
|
||||
await queryInterface.sequelize.query(
|
||||
`UPDATE permission_nodes SET parent_id = NULL`,
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
// 5. Delete new permission nodes
|
||||
await queryInterface.sequelize.query(
|
||||
`DELETE FROM permission_nodes WHERE node_code IN ('settings.tenants', 'notifications')`,
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
// 6. Delete parent groups
|
||||
await queryInterface.sequelize.query(
|
||||
`DELETE FROM permission_nodes WHERE node_code IN ('products', 'masters', 'settings', 'reports')`,
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,190 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
// 1. Locate existing legacy permission nodes
|
||||
const legacyNodes = await queryInterface.sequelize.query(
|
||||
`SELECT id, node_code, node_name FROM permission_nodes WHERE node_code IN ('PIM_SYSTEM', 'USERS_MANAGEMENT', 'ROLES_MANAGEMENT', 'TENANTS_MANAGEMENT')`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
console.log('--- LEGACY RBAC PROJECT AUDIT & LOGGING ---');
|
||||
console.log('Affected Permission Nodes:', legacyNodes);
|
||||
|
||||
if (legacyNodes.length > 0) {
|
||||
const legacyNodeIds = legacyNodes.map(n => n.id);
|
||||
|
||||
// Fetch and log affected role_permissions mapping records
|
||||
const affectedMappings = await queryInterface.sequelize.query(
|
||||
`SELECT rp.role_id, rp.node_id, pn.node_code, rp.can_view, rp.can_create, rp.can_edit, rp.can_delete
|
||||
FROM role_permissions rp
|
||||
JOIN permission_nodes pn ON rp.node_id = pn.id
|
||||
WHERE rp.node_id IN (${legacyNodeIds.join(',')})`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
console.log('Affected Role Permission mappings:', affectedMappings);
|
||||
|
||||
// 2. Create the backup table to hold mappings for potential rollback
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE IF NOT EXISTS _backup_legacy_role_permissions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
role_id INTEGER NOT NULL,
|
||||
node_code VARCHAR(50) NOT NULL,
|
||||
can_view BOOLEAN DEFAULT false,
|
||||
can_create BOOLEAN DEFAULT false,
|
||||
can_edit BOOLEAN DEFAULT false,
|
||||
can_delete BOOLEAN DEFAULT false,
|
||||
can_alter BOOLEAN DEFAULT false,
|
||||
can_export BOOLEAN DEFAULT false,
|
||||
can_import BOOLEAN DEFAULT false
|
||||
)
|
||||
`, { transaction });
|
||||
|
||||
// Backup existing mappings dynamically (idempotent: avoid duplicate inserts if rerun)
|
||||
for (const mapping of affectedMappings) {
|
||||
const [exists] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM _backup_legacy_role_permissions WHERE role_id = ${mapping.role_id} AND node_code = '${mapping.node_code}'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
if (!exists) {
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO _backup_legacy_role_permissions (role_id, node_code, can_view, can_create, can_edit, can_delete, can_alter, can_export, can_import)
|
||||
VALUES (
|
||||
${mapping.role_id},
|
||||
'${mapping.node_code}',
|
||||
${mapping.can_view},
|
||||
${mapping.can_create},
|
||||
${mapping.can_edit},
|
||||
${mapping.can_delete},
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
`, { transaction });
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Safely delete legacy role permissions
|
||||
await queryInterface.sequelize.query(
|
||||
`DELETE FROM role_permissions WHERE node_id IN (${legacyNodeIds.join(',')})`,
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
// 4. Safely delete legacy permission nodes (cascade check handled by deleting role_permissions first)
|
||||
await queryInterface.sequelize.query(
|
||||
`DELETE FROM permission_nodes WHERE id IN (${legacyNodeIds.join(',')})`,
|
||||
{ transaction }
|
||||
);
|
||||
|
||||
console.log('Legacy permissions and mappings successfully cleared.');
|
||||
} else {
|
||||
console.log('No legacy permission nodes found. Skipping deletion.');
|
||||
}
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('Migration UP failed, transaction rolled back:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
try {
|
||||
const now = new Date();
|
||||
|
||||
// 1. Re-create root node PIM_SYSTEM if not exists
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO permission_nodes (node_code, node_name, node_type, parent_id, node_level, display_order, can_view, can_create, can_edit, can_delete, created_at, updated_at)
|
||||
VALUES ('PIM_SYSTEM', 'PIM System', 'module', NULL, 1, 1, true, false, false, false, NOW(), NOW())
|
||||
ON CONFLICT (node_code) DO NOTHING
|
||||
`, { transaction });
|
||||
|
||||
// Fetch root node ID
|
||||
const [pimNode] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM permission_nodes WHERE node_code = 'PIM_SYSTEM'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (!pimNode) {
|
||||
throw new Error('Failed to retrieve or create PIM_SYSTEM permission node during rollback.');
|
||||
}
|
||||
|
||||
// 2. Re-create child feature nodes USERS_MANAGEMENT, ROLES_MANAGEMENT, TENANTS_MANAGEMENT
|
||||
const children = [
|
||||
{ code: 'USERS_MANAGEMENT', name: 'Users Management', order: 1 },
|
||||
{ code: 'ROLES_MANAGEMENT', name: 'Roles Management', order: 2 },
|
||||
{ code: 'TENANTS_MANAGEMENT', name: 'Tenants Management', order: 3 }
|
||||
];
|
||||
|
||||
for (const child of children) {
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO permission_nodes (node_code, node_name, node_type, parent_id, node_level, display_order, can_view, can_create, can_edit, can_delete, created_at, updated_at)
|
||||
VALUES ('${child.code}', '${child.name}', 'feature', ${pimNode.id}, 2, ${child.order}, true, true, true, true, NOW(), NOW())
|
||||
ON CONFLICT (node_code) DO NOTHING
|
||||
`, { transaction });
|
||||
}
|
||||
|
||||
// 3. Restore role mappings from backup table if it exists
|
||||
const tableExists = await queryInterface.sequelize.query(
|
||||
`SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = '_backup_legacy_role_permissions')`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (tableExists[0]?.exists) {
|
||||
const backups = await queryInterface.sequelize.query(
|
||||
`SELECT role_id, node_code, can_view, can_create, can_edit, can_delete, can_alter, can_export, can_import FROM _backup_legacy_role_permissions`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
for (const backup of backups) {
|
||||
const [node] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM permission_nodes WHERE node_code = '${backup.node_code}'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (node) {
|
||||
// Check if active mapping exists to prevent duplicate insertion
|
||||
const [exists] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM role_permissions WHERE role_id = ${backup.role_id} AND node_id = ${node.id}`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (!exists) {
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO role_permissions (role_id, node_id, can_view, can_create, can_edit, can_delete, can_alter, can_export, can_import, created_at, updated_at)
|
||||
VALUES (
|
||||
${backup.role_id},
|
||||
${node.id},
|
||||
${backup.can_view},
|
||||
${backup.can_create},
|
||||
${backup.can_edit},
|
||||
${backup.can_delete},
|
||||
${backup.can_alter},
|
||||
${backup.can_export},
|
||||
${backup.can_import},
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
`, { transaction });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up the backup table post rollback
|
||||
await queryInterface.sequelize.query(`DROP TABLE IF EXISTS _backup_legacy_role_permissions`, { transaction });
|
||||
console.log('Legacy permissions restored and backup table dropped.');
|
||||
}
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('Migration DOWN failed, transaction rolled back:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -4,124 +4,105 @@ const bcrypt = require('bcrypt');
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
const transaction = await queryInterface.sequelize.transaction();
|
||||
|
||||
|
||||
try {
|
||||
// 1. Create Root Permission Node (PIM System)
|
||||
const [pimSystemNode] = await queryInterface.bulkInsert('permission_nodes', [{
|
||||
node_code: 'PIM_SYSTEM',
|
||||
node_name: 'PIM System',
|
||||
node_type: 'module',
|
||||
parent_id: null,
|
||||
node_level: 1,
|
||||
display_order: 1,
|
||||
can_view: true,
|
||||
can_create: false,
|
||||
can_edit: false,
|
||||
can_delete: false,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
}], { returning: true, transaction });
|
||||
const now = new Date();
|
||||
|
||||
// 2. Create child nodes (Settings, Users, Roles)
|
||||
const childNodes = await queryInterface.bulkInsert('permission_nodes', [
|
||||
{
|
||||
node_code: 'USERS_MANAGEMENT',
|
||||
node_name: 'Users Management',
|
||||
node_type: 'feature',
|
||||
parent_id: pimSystemNode.id,
|
||||
node_level: 2,
|
||||
display_order: 1,
|
||||
can_view: true,
|
||||
can_create: true,
|
||||
can_edit: true,
|
||||
can_delete: true,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
},
|
||||
{
|
||||
node_code: 'ROLES_MANAGEMENT',
|
||||
node_name: 'Roles Management',
|
||||
node_type: 'feature',
|
||||
parent_id: pimSystemNode.id,
|
||||
node_level: 2,
|
||||
display_order: 2,
|
||||
can_view: true,
|
||||
can_create: true,
|
||||
can_edit: true,
|
||||
can_delete: true,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
},
|
||||
{
|
||||
node_code: 'TENANTS_MANAGEMENT',
|
||||
node_name: 'Tenants Management',
|
||||
node_type: 'feature',
|
||||
parent_id: pimSystemNode.id,
|
||||
node_level: 2,
|
||||
display_order: 3,
|
||||
can_view: true,
|
||||
can_create: true,
|
||||
can_edit: true,
|
||||
can_delete: true,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
// 1. Create Super Admin Role
|
||||
let superAdminRoleId;
|
||||
const [existingRole] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM roles WHERE role_code = 'SUPER_ADMIN'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (existingRole) {
|
||||
superAdminRoleId = existingRole.id;
|
||||
} else {
|
||||
const [newRole] = await queryInterface.bulkInsert('roles', [{
|
||||
tenant_id: null,
|
||||
role_code: 'SUPER_ADMIN',
|
||||
role_name: 'Super Admin',
|
||||
description: 'System Administrator with full access to all tenant and platform features',
|
||||
role_type: 'platform',
|
||||
is_system_role: true,
|
||||
status: true,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}], { returning: true, transaction });
|
||||
superAdminRoleId = newRole.id;
|
||||
}
|
||||
|
||||
// Fetch all existing permission nodes to map them to Super Admin role
|
||||
const allActiveNodes = await queryInterface.sequelize.query(
|
||||
`SELECT id, node_code FROM permission_nodes`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
// 2. Assign all permissions to Super Admin role
|
||||
for (const node of allActiveNodes) {
|
||||
const [existingMapping] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM role_permissions WHERE role_id = ${superAdminRoleId} AND node_id = ${node.id}`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (!existingMapping) {
|
||||
await queryInterface.bulkInsert('role_permissions', [{
|
||||
role_id: superAdminRoleId,
|
||||
node_id: node.id,
|
||||
can_view: true,
|
||||
can_create: true,
|
||||
can_edit: true,
|
||||
can_delete: true,
|
||||
can_alter: true,
|
||||
can_export: true,
|
||||
can_import: true,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}], { transaction });
|
||||
}
|
||||
], { returning: true, transaction });
|
||||
|
||||
// 3. Create Super Admin Role
|
||||
const [superAdminRole] = await queryInterface.bulkInsert('roles', [{
|
||||
tenant_id: null,
|
||||
role_code: 'SUPER_ADMIN',
|
||||
role_name: 'Super Admin',
|
||||
description: 'System Administrator with full access to all tenant and platform features',
|
||||
role_type: 'platform',
|
||||
is_system_role: true,
|
||||
status: true,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
}], { returning: true, transaction });
|
||||
|
||||
// 4. Assign all permissions to Super Admin role
|
||||
const allNodes = [pimSystemNode, ...childNodes];
|
||||
const rolePermissions = allNodes.map(node => ({
|
||||
role_id: superAdminRole.id,
|
||||
node_id: node.id,
|
||||
can_view: node.can_view,
|
||||
can_create: node.can_create,
|
||||
can_edit: node.can_edit,
|
||||
can_delete: node.can_delete,
|
||||
can_alter: false,
|
||||
can_export: false,
|
||||
can_import: false,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
}));
|
||||
|
||||
await queryInterface.bulkInsert('role_permissions', rolePermissions, { transaction });
|
||||
}
|
||||
|
||||
// 5. Create Super Admin User
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
const hashedPassword = await bcrypt.hash('Admin@123', salt);
|
||||
let superAdminUserId;
|
||||
const [existingUser] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM users WHERE email = 'superadmin@maskan.com'`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
const [superAdminUser] = await queryInterface.bulkInsert('users', [{
|
||||
tenant_id: null,
|
||||
email: 'superadmin@maskan.com',
|
||||
user_name: 'Super Admin',
|
||||
user_type: 'platform',
|
||||
password_hash: hashedPassword,
|
||||
status: true,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
}], { returning: true, transaction });
|
||||
if (existingUser) {
|
||||
superAdminUserId = existingUser.id;
|
||||
} else {
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
const hashedPassword = await bcrypt.hash('Admin@123', salt);
|
||||
|
||||
const [newUser] = await queryInterface.bulkInsert('users', [{
|
||||
tenant_id: null,
|
||||
email: 'superadmin@maskan.com',
|
||||
user_name: 'Super Admin',
|
||||
user_type: 'platform',
|
||||
password_hash: hashedPassword,
|
||||
status: true,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}], { returning: true, transaction });
|
||||
superAdminUserId = newUser.id;
|
||||
}
|
||||
|
||||
// 6. Assign Super Admin Role to Super Admin User
|
||||
await queryInterface.bulkInsert('user_roles', [{
|
||||
user_id: superAdminUser.id,
|
||||
role_id: superAdminRole.id,
|
||||
status: true,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
}], { transaction });
|
||||
const [existingUserRole] = await queryInterface.sequelize.query(
|
||||
`SELECT id FROM user_roles WHERE user_id = ${superAdminUserId} AND role_id = ${superAdminRoleId}`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT, transaction }
|
||||
);
|
||||
|
||||
if (!existingUserRole) {
|
||||
await queryInterface.bulkInsert('user_roles', [{
|
||||
user_id: superAdminUserId,
|
||||
role_id: superAdminRoleId,
|
||||
status: true,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}], { transaction });
|
||||
}
|
||||
|
||||
await transaction.commit();
|
||||
} catch (error) {
|
||||
|
||||
@@ -18,13 +18,13 @@ module.exports = {
|
||||
('products', 'Products', 'group', NULL, 1, 10, true, false, false, false, false, false, false, '${now}', '${now}'),
|
||||
('masters', 'Masters', 'group', NULL, 1, 20, true, false, false, false, false, false, false, '${now}', '${now}'),
|
||||
('settings', 'Settings', 'group', NULL, 1, 30, true, false, false, false, false, false, false, '${now}', '${now}'),
|
||||
('reports', 'Reports', 'group', NULL, 1, 40, true, false, false, false, false, false, false, '${now}', '${now}')
|
||||
('reports', 'Reports', 'feature', NULL, 1, 40, true, true, true, true, false, true, false, '${now}', '${now}')
|
||||
ON CONFLICT (node_code) DO NOTHING;
|
||||
`);
|
||||
|
||||
// Fetch parent IDs
|
||||
const parents = await queryInterface.sequelize.query(
|
||||
`SELECT id, node_code FROM permission_nodes WHERE node_code IN ('products','masters','settings','reports')`,
|
||||
`SELECT id, node_code FROM permission_nodes WHERE node_code IN ('products','masters','settings')`,
|
||||
{ type: queryInterface.sequelize.QueryTypes.SELECT }
|
||||
);
|
||||
const pid = {};
|
||||
@@ -45,9 +45,11 @@ module.exports = {
|
||||
('settings.users', 'Users', 'feature', '${pid['settings']}', 2, 31, true, true, true, true, false, false, false, '${now}', '${now}'),
|
||||
('settings.roles', 'Roles', 'feature', '${pid['settings']}', 2, 32, true, true, true, true, false, false, false, '${now}', '${now}'),
|
||||
('settings.integrations', 'Integrations', 'feature', '${pid['settings']}', 2, 33, true, true, true, true, false, false, false, '${now}', '${now}'),
|
||||
('reports.sales', 'Sales Reports', 'feature', '${pid['reports']}', 2, 41, true, false, false, false, false, true, false, '${now}', '${now}'),
|
||||
('reports.inventory', 'Inventory Reports', 'feature', '${pid['reports']}', 2, 42, true, false, false, false, false, true, false, '${now}', '${now}')
|
||||
ON CONFLICT (node_code) DO NOTHING;
|
||||
('settings.tenants', 'Tenants', 'feature', '${pid['settings']}', 2, 35, true, true, true, true, false, false, false, '${now}', '${now}')
|
||||
ON CONFLICT (node_code) DO UPDATE SET
|
||||
parent_id = EXCLUDED.parent_id,
|
||||
node_level = EXCLUDED.node_level,
|
||||
display_order = EXCLUDED.display_order;
|
||||
`);
|
||||
},
|
||||
|
||||
@@ -58,7 +60,7 @@ module.exports = {
|
||||
'products.items', 'products.variants', 'products.families',
|
||||
'products.categories', 'products.attributes',
|
||||
'masters.brands', 'masters.units',
|
||||
'settings.users', 'settings.roles', 'settings.integrations',
|
||||
'settings.users', 'settings.roles', 'settings.integrations', 'settings.tenants',
|
||||
'reports.sales', 'reports.inventory',
|
||||
],
|
||||
}, {});
|
||||
|
||||
@@ -19,7 +19,10 @@ module.exports = {
|
||||
'notifications', 'Notifications', 'feature', ${parentId ? `'${parentId}'` : 'NULL'}, 2, 34,
|
||||
true, false, true, false, false, false, false, '${now}', '${now}'
|
||||
)
|
||||
ON CONFLICT (node_code) DO NOTHING;
|
||||
ON CONFLICT (node_code) DO UPDATE SET
|
||||
parent_id = EXCLUDED.parent_id,
|
||||
node_level = EXCLUDED.node_level,
|
||||
display_order = EXCLUDED.display_order;
|
||||
`);
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user