import { Injectable, Logger } from "@nestjs/common"; import { Organization, OrganizationConfiguration, Permission, Position, PositionPermission, PositionType, Role, RolePermission, Unit, } from "@tria-plc/iamapi-common"; import { DataSource, EntityManager, In } from "typeorm"; import { ERoleKey } from "@tria-plc/api-common/utils/enums/seed.enum"; import { BOOKING_RULE_ENGINE_PERMISSION_KEYS } from "./freight-permissions.registry"; import { EDR_FREIGHT_POSITIONS, EDR_FREIGHT_ROLES, type FreightSeedPosition, type FreightSeedRole, } from "./edr-freight.seed"; const EDR_UNIT_KEY = "edr_freight_hq"; const EDR_UNIT_NAME = { en: "EDR Freight HQ" }; const EDR_POSITION_TYPE_KEY = "edr_freight_role"; const EDR_POSITION_TYPE_NAME = { en: "EDR Freight Role" }; const EDR_ORG_KEY = "edr_freight"; const EDR_ORG_NAME = { en: "EDR Freight" }; const SEED_FLAG = "SEED_EDR_ORG"; type SeedOrganization = { id: string; key: string; }; @Injectable() export class EdrOrgSeeder { private readonly logger = new Logger(EdrOrgSeeder.name); constructor(private readonly dataSource: DataSource) {} async run() { if (!this.shouldSeed()) { this.logger.log(`Skipping EDR org seed because ${SEED_FLAG} is not enabled`); return; } await this.dataSource.transaction(async (manager) => { const organization = await this.ensureOrganization(manager); await this.ensureOrganizationConfiguration(manager, organization.id); await this.ensureRoles(manager, EDR_FREIGHT_ROLES); await this.ensureRolePermissions(manager, EDR_FREIGHT_ROLES); await this.ensureSuperAdminPermissions(manager); // Positions-as-roles: seed operational positions and grant their // permissions via PositionPermission (not Role/RolePermission). const unit = await this.ensureDefaultUnit(manager, organization.id); const positionType = await this.ensureDefaultPositionType(manager, unit.id); await this.ensurePositions( manager, organization.id, unit.id, positionType.id, EDR_FREIGHT_POSITIONS, ); await this.ensurePositionPermissions(manager, unit.id, EDR_FREIGHT_POSITIONS); }); this.logger.log(`Ensured EDR organization seed for '${EDR_ORG_KEY}'`); } private shouldSeed() { return process.env[SEED_FLAG]?.trim().toLowerCase() === "true"; } private async ensureOrganization( manager: EntityManager, ): Promise { const organizationRepository = manager.getRepository(Organization); let organization = await organizationRepository.findOne({ where: { key: EDR_ORG_KEY }, select: { id: true, key: true }, }); if (!organization) { const insertResult = await organizationRepository.insert({ key: EDR_ORG_KEY, name: EDR_ORG_NAME, isGovernmentOrganization: true, }); this.logger.log(`Seeded EDR organization '${EDR_ORG_KEY}'`); return { id: insertResult.identifiers[0]?.id as string, key: EDR_ORG_KEY, }; } this.logger.log(`Ensured EDR organization '${EDR_ORG_KEY}'`); return { id: organization.id as string, key: EDR_ORG_KEY, }; } private async ensureOrganizationConfiguration( manager: EntityManager, organizationId: string, ) { const organizationConfigurationRepository = manager.getRepository(OrganizationConfiguration); await organizationConfigurationRepository.upsert({ organizationId, canCreateBranchByItself: true, canStartReceivingRecord: true, }, { conflictPaths: { organizationId: true }, }); this.logger.log( `Ensured organization configuration for '${EDR_ORG_KEY}'`, ); } private async ensureRoles(manager: EntityManager, seedRoles: FreightSeedRole[]) { await manager.getRepository(Role).upsert( seedRoles.map(({ key, name }) => ({ key, name })), { conflictPaths: { key: true }, }, ); this.logger.log( `Ensured EDR roles '${seedRoles.map((role) => role.key).join("', '")}'`, ); } private async ensureRolePermissions( manager: EntityManager, seedRoles: FreightSeedRole[], ) { const permissionKeys = [...new Set(seedRoles.flatMap((role) => role.permissionKeys))]; if (!permissionKeys.length) { this.logger.log("No EDR role permissions configured; skipping role-permission links"); return; } const roleRepository = manager.getRepository(Role); const rolePermissionRepository = manager.getRepository(RolePermission); const roles = await roleRepository.find({ where: { key: In(seedRoles.map((role) => role.key)) }, select: { id: true, key: true }, }); const seededPermissions = await manager.getRepository(Permission).find({ where: { key: In(permissionKeys) }, select: { id: true, key: true }, }); const roleByKey = new Map(roles.map((role) => [role.key, role])); const permissionByKey = new Map( seededPermissions.map((permission) => [permission.key, permission]), ); const rolePermissions = seedRoles.flatMap((role) => { const seededRole = roleByKey.get(role.key); if (!seededRole) { throw new Error(`missing_role:${role.key}`); } return role.permissionKeys.map((permissionKey) => { const seededPermission = permissionByKey.get(permissionKey); if (!seededPermission) { throw new Error(`missing_permission:${permissionKey}`); } return { roleId: seededRole.id, permissionId: seededPermission.id, }; }); }); await rolePermissionRepository.upsert(rolePermissions, { conflictPaths: { roleId: true, permissionId: true }, }); this.logger.log(`Ensured ${rolePermissions.length} EDR role-permission links`); } private async ensureSuperAdminPermissions(manager: EntityManager) { const role = await manager.getRepository(Role).findOne({ where: { key: ERoleKey.SUPER_ADMIN }, select: { id: true, key: true }, }); if (!role) { this.logger.warn( `Role ${ERoleKey.SUPER_ADMIN} not found; skipping booking/rule-engine super_admin links`, ); return; } const permissions = await manager.getRepository(Permission).find({ where: { key: In(BOOKING_RULE_ENGINE_PERMISSION_KEYS) }, select: { id: true, key: true }, }); if (!permissions.length) { this.logger.warn('No booking/rule-engine permissions found for super_admin'); return; } await manager.getRepository(RolePermission).upsert( permissions.map((permission) => ({ roleId: role.id, permissionId: permission.id, })), { conflictPaths: { roleId: true, permissionId: true } }, ); this.logger.log( `Ensured ${permissions.length} booking+rule-engine permissions on super_admin`, ); } private async ensureDefaultUnit( manager: EntityManager, organizationId: string, ): Promise<{ id: string }> { const unitRepository = manager.getRepository(Unit); let unit = await unitRepository.findOne({ where: { key: EDR_UNIT_KEY, organizationId }, select: { id: true }, }); if (!unit) { const insertResult = await unitRepository.insert({ key: EDR_UNIT_KEY, name: EDR_UNIT_NAME, organizationId, }); this.logger.log(`Seeded EDR unit '${EDR_UNIT_KEY}'`); return { id: insertResult.identifiers[0]?.id as string }; } this.logger.log(`Ensured EDR unit '${EDR_UNIT_KEY}'`); return { id: unit.id }; } private async ensureDefaultPositionType( manager: EntityManager, unitId: string, ): Promise<{ id: string }> { const positionTypeRepository = manager.getRepository(PositionType); // PositionType has no unique constraint on (key, unitId); find-then-insert. let positionType = await positionTypeRepository.findOne({ where: { key: EDR_POSITION_TYPE_KEY, unitId }, select: { id: true }, }); if (!positionType) { const insertResult = await positionTypeRepository.insert({ key: EDR_POSITION_TYPE_KEY, name: EDR_POSITION_TYPE_NAME, isSystem: true, unitId, }); this.logger.log(`Seeded EDR position type '${EDR_POSITION_TYPE_KEY}'`); return { id: insertResult.identifiers[0]?.id as string }; } this.logger.log(`Ensured EDR position type '${EDR_POSITION_TYPE_KEY}'`); return { id: positionType.id }; } private async ensurePositions( manager: EntityManager, organizationId: string, unitId: string, positionTypeId: string, seedPositions: FreightSeedPosition[], ) { await manager.getRepository(Position).upsert( seedPositions.map(({ key, name, rank }) => ({ key, name, rank, organizationId, unitId, positionTypeId, })), { conflictPaths: { key: true, unitId: true }, }, ); this.logger.log( `Ensured ${seedPositions.length} EDR positions '${seedPositions .map((position) => position.key) .join("', '")}'`, ); } private async ensurePositionPermissions( manager: EntityManager, unitId: string, seedPositions: FreightSeedPosition[], ) { const permissionKeys = [ ...new Set(seedPositions.flatMap((position) => position.permissionKeys)), ]; if (!permissionKeys.length) { this.logger.log( "No EDR position permissions configured; skipping position-permission links", ); return; } const positions = await manager.getRepository(Position).find({ where: { key: In(seedPositions.map((position) => position.key)), unitId }, select: { id: true, key: true }, }); const seededPermissions = await manager.getRepository(Permission).find({ where: { key: In(permissionKeys) }, select: { id: true, key: true }, }); const positionByKey = new Map( positions.map((position) => [position.key, position]), ); const permissionByKey = new Map( seededPermissions.map((permission) => [permission.key, permission]), ); const positionPermissions = seedPositions.flatMap((position) => { const seededPosition = positionByKey.get(position.key); if (!seededPosition) { throw new Error(`missing_position:${position.key}`); } return position.permissionKeys.map((permissionKey) => { const seededPermission = permissionByKey.get(permissionKey); if (!seededPermission) { throw new Error(`missing_permission:${permissionKey}`); } return { positionId: seededPosition.id as string, permissionId: seededPermission.id, }; }); }); await manager.getRepository(PositionPermission).upsert(positionPermissions, { conflictPaths: { positionId: true, permissionId: true }, }); this.logger.log( `Ensured ${positionPermissions.length} EDR position-permission links`, ); } }