247 lines
7.0 KiB
JavaScript
247 lines
7.0 KiB
JavaScript
'use strict';
|
|
|
|
const { v4: uuid } = require('uuid');
|
|
const { QueryTypes } = require('sequelize');
|
|
|
|
const BUSINESS_ROLES = {
|
|
SUPER_ADMIN: 'Super Administrator',
|
|
ADMIN: 'Administrator',
|
|
CONCIERGE: 'Concierge Coordinator',
|
|
CUSTOMER: 'Customer',
|
|
PLATFORM_OWNER: 'Platform Owner',
|
|
OPERATIONS_DIRECTOR: 'Operations Director',
|
|
RESERVATIONS_LEAD: 'Reservations Lead',
|
|
FINANCE_CONTROLLER: 'Finance Controller',
|
|
};
|
|
|
|
const rolePermissionMatrix = {
|
|
[BUSINESS_ROLES.ADMIN]: [
|
|
'CREATE_BOOKING_REQUESTS',
|
|
'READ_BOOKING_REQUESTS',
|
|
'UPDATE_BOOKING_REQUESTS',
|
|
'READ_APPROVAL_STEPS',
|
|
'UPDATE_APPROVAL_STEPS',
|
|
'CREATE_RESERVATIONS',
|
|
'READ_RESERVATIONS',
|
|
'UPDATE_RESERVATIONS',
|
|
'CREATE_SERVICE_REQUESTS',
|
|
'READ_SERVICE_REQUESTS',
|
|
'UPDATE_SERVICE_REQUESTS',
|
|
'READ_INVOICES',
|
|
'CREATE_DOCUMENTS',
|
|
'READ_DOCUMENTS',
|
|
'UPDATE_DOCUMENTS',
|
|
'READ_ORGANIZATIONS',
|
|
'READ_TENANTS',
|
|
'READ_PROPERTIES',
|
|
'READ_UNITS',
|
|
'READ_NEGOTIATED_RATES',
|
|
],
|
|
[BUSINESS_ROLES.CONCIERGE]: [
|
|
'CREATE_BOOKING_REQUESTS',
|
|
'READ_BOOKING_REQUESTS',
|
|
'UPDATE_BOOKING_REQUESTS',
|
|
'READ_RESERVATIONS',
|
|
'CREATE_SERVICE_REQUESTS',
|
|
'READ_SERVICE_REQUESTS',
|
|
'UPDATE_SERVICE_REQUESTS',
|
|
'CREATE_DOCUMENTS',
|
|
'READ_DOCUMENTS',
|
|
'UPDATE_DOCUMENTS',
|
|
'READ_PROPERTIES',
|
|
'READ_UNITS',
|
|
],
|
|
[BUSINESS_ROLES.CUSTOMER]: [
|
|
'CREATE_BOOKING_REQUESTS',
|
|
'READ_BOOKING_REQUESTS',
|
|
'UPDATE_BOOKING_REQUESTS',
|
|
'READ_RESERVATIONS',
|
|
'CREATE_SERVICE_REQUESTS',
|
|
'READ_SERVICE_REQUESTS',
|
|
'UPDATE_SERVICE_REQUESTS',
|
|
'READ_DOCUMENTS',
|
|
],
|
|
[BUSINESS_ROLES.PLATFORM_OWNER]: [
|
|
'CREATE_BOOKING_REQUESTS',
|
|
'READ_BOOKING_REQUESTS',
|
|
'UPDATE_BOOKING_REQUESTS',
|
|
'READ_APPROVAL_STEPS',
|
|
'UPDATE_APPROVAL_STEPS',
|
|
'CREATE_RESERVATIONS',
|
|
'READ_RESERVATIONS',
|
|
'UPDATE_RESERVATIONS',
|
|
'CREATE_SERVICE_REQUESTS',
|
|
'READ_SERVICE_REQUESTS',
|
|
'UPDATE_SERVICE_REQUESTS',
|
|
'READ_INVOICES',
|
|
'CREATE_DOCUMENTS',
|
|
'READ_DOCUMENTS',
|
|
'UPDATE_DOCUMENTS',
|
|
'READ_ORGANIZATIONS',
|
|
'READ_PROPERTIES',
|
|
'READ_UNITS',
|
|
'READ_NEGOTIATED_RATES',
|
|
],
|
|
[BUSINESS_ROLES.OPERATIONS_DIRECTOR]: [
|
|
'CREATE_BOOKING_REQUESTS',
|
|
'READ_BOOKING_REQUESTS',
|
|
'UPDATE_BOOKING_REQUESTS',
|
|
'READ_APPROVAL_STEPS',
|
|
'UPDATE_APPROVAL_STEPS',
|
|
'CREATE_RESERVATIONS',
|
|
'READ_RESERVATIONS',
|
|
'UPDATE_RESERVATIONS',
|
|
'CREATE_SERVICE_REQUESTS',
|
|
'READ_SERVICE_REQUESTS',
|
|
'UPDATE_SERVICE_REQUESTS',
|
|
'CREATE_DOCUMENTS',
|
|
'READ_DOCUMENTS',
|
|
'UPDATE_DOCUMENTS',
|
|
'READ_PROPERTIES',
|
|
'READ_UNITS',
|
|
'READ_NEGOTIATED_RATES',
|
|
],
|
|
[BUSINESS_ROLES.RESERVATIONS_LEAD]: [
|
|
'CREATE_BOOKING_REQUESTS',
|
|
'READ_BOOKING_REQUESTS',
|
|
'UPDATE_BOOKING_REQUESTS',
|
|
'READ_APPROVAL_STEPS',
|
|
'UPDATE_APPROVAL_STEPS',
|
|
'CREATE_RESERVATIONS',
|
|
'READ_RESERVATIONS',
|
|
'UPDATE_RESERVATIONS',
|
|
'CREATE_SERVICE_REQUESTS',
|
|
'READ_SERVICE_REQUESTS',
|
|
'UPDATE_SERVICE_REQUESTS',
|
|
'READ_DOCUMENTS',
|
|
'READ_PROPERTIES',
|
|
'READ_UNITS',
|
|
'READ_NEGOTIATED_RATES',
|
|
],
|
|
[BUSINESS_ROLES.FINANCE_CONTROLLER]: [
|
|
'READ_BOOKING_REQUESTS',
|
|
'READ_RESERVATIONS',
|
|
'READ_INVOICES',
|
|
'UPDATE_INVOICES',
|
|
'READ_DOCUMENTS',
|
|
],
|
|
};
|
|
|
|
async function findRoleByName(queryInterface, name) {
|
|
const rows = await queryInterface.sequelize.query(
|
|
'SELECT "id", "name" FROM "roles" WHERE "name" = :name LIMIT 1',
|
|
{
|
|
replacements: { name },
|
|
type: QueryTypes.SELECT,
|
|
},
|
|
);
|
|
|
|
return rows[0] || null;
|
|
}
|
|
|
|
async function ensureRole(queryInterface, name, now) {
|
|
const existingRole = await findRoleByName(queryInterface, name);
|
|
|
|
if (existingRole) {
|
|
return existingRole.id;
|
|
}
|
|
|
|
const id = uuid();
|
|
await queryInterface.bulkInsert('roles', [
|
|
{
|
|
id,
|
|
name,
|
|
globalAccess: false,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
},
|
|
]);
|
|
|
|
return id;
|
|
}
|
|
|
|
async function getPermissionIdMap(queryInterface, permissionNames) {
|
|
const permissions = await queryInterface.sequelize.query(
|
|
'SELECT "id", "name" FROM "permissions" WHERE "name" IN (:permissionNames)',
|
|
{
|
|
replacements: { permissionNames },
|
|
type: QueryTypes.SELECT,
|
|
},
|
|
);
|
|
|
|
return new Map(permissions.map((permission) => [permission.name, permission.id]));
|
|
}
|
|
|
|
module.exports = {
|
|
async up(queryInterface) {
|
|
const now = new Date();
|
|
|
|
const customerRoleId = await ensureRole(queryInterface, BUSINESS_ROLES.CUSTOMER, now);
|
|
|
|
await queryInterface.bulkUpdate('roles', { globalAccess: true, updatedAt: now }, { name: BUSINESS_ROLES.SUPER_ADMIN });
|
|
await queryInterface.sequelize.query(
|
|
'UPDATE "roles" SET "globalAccess" = false, "updatedAt" = :updatedAt WHERE "name" IN (:roleNames)',
|
|
{
|
|
replacements: {
|
|
updatedAt: now,
|
|
roleNames: [
|
|
BUSINESS_ROLES.ADMIN,
|
|
BUSINESS_ROLES.CONCIERGE,
|
|
BUSINESS_ROLES.CUSTOMER,
|
|
BUSINESS_ROLES.PLATFORM_OWNER,
|
|
BUSINESS_ROLES.OPERATIONS_DIRECTOR,
|
|
BUSINESS_ROLES.RESERVATIONS_LEAD,
|
|
BUSINESS_ROLES.FINANCE_CONTROLLER,
|
|
],
|
|
},
|
|
},
|
|
);
|
|
|
|
const roleIds = {};
|
|
for (const roleName of Object.keys(rolePermissionMatrix)) {
|
|
const role = await findRoleByName(queryInterface, roleName);
|
|
if (!role) {
|
|
throw new Error(`Role '${roleName}' was not found while aligning the business role matrix.`);
|
|
}
|
|
roleIds[roleName] = role.id;
|
|
}
|
|
roleIds[BUSINESS_ROLES.CUSTOMER] = customerRoleId;
|
|
|
|
const permissionNames = [...new Set(Object.values(rolePermissionMatrix).flat())];
|
|
const permissionIdMap = await getPermissionIdMap(queryInterface, permissionNames);
|
|
const missingPermissions = permissionNames.filter((permissionName) => !permissionIdMap.get(permissionName));
|
|
|
|
if (missingPermissions.length > 0) {
|
|
throw new Error(`Missing permissions for role matrix alignment: ${missingPermissions.join(', ')}`);
|
|
}
|
|
|
|
const impactedRoleIds = Object.values(roleIds);
|
|
await queryInterface.sequelize.query(
|
|
'DELETE FROM "rolesPermissionsPermissions" WHERE "roles_permissionsId" IN (:roleIds)',
|
|
{
|
|
replacements: { roleIds: impactedRoleIds },
|
|
},
|
|
);
|
|
|
|
const rows = Object.entries(rolePermissionMatrix).flatMap(([roleName, permissions]) =>
|
|
permissions.map((permissionName) => ({
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
roles_permissionsId: roleIds[roleName],
|
|
permissionId: permissionIdMap.get(permissionName),
|
|
})),
|
|
);
|
|
|
|
if (rows.length > 0) {
|
|
await queryInterface.bulkInsert('rolesPermissionsPermissions', rows);
|
|
}
|
|
|
|
await queryInterface.bulkUpdate('users', { app_roleId: customerRoleId, updatedAt: now }, { email: 'client@hello.com' });
|
|
await queryInterface.bulkUpdate('users', { app_roleId: roleIds[BUSINESS_ROLES.CONCIERGE], updatedAt: now }, { email: 'john@doe.com' });
|
|
},
|
|
|
|
async down() {
|
|
// Intentionally left blank. This seeder aligns live business roles and should not blindly revert production data.
|
|
},
|
|
};
|