import { Injectable, Logger } from "@nestjs/common"; import { Organization, OrganizationConfiguration, Permission, Role, RolePermission, } 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_ROLES, type FreightSeedRole } from "./edr-freight.seed"; 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); }); 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`, ); } }