mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 21:08:12 +00:00
228 lines
9.0 KiB
TypeScript
228 lines
9.0 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
|
import {
|
|
Application,
|
|
Organization,
|
|
OrganizationConfiguration,
|
|
Permission,
|
|
Role,
|
|
RolePermission,
|
|
} from '@tria-plc/iamapi-common';
|
|
import { InjectDataSource } from '@nestjs/typeorm';
|
|
import { DataSource, EntityManager, In } from 'typeorm';
|
|
import { ERoleKey } from '@tria-plc/api-common/utils/enums/seed.enum';
|
|
import {
|
|
PASSENGER_PERMISSIONS,
|
|
PASSENGER_PERMISSION_KEYS,
|
|
} from './passenger-permissions.registry';
|
|
import { EDR_PASSENGER_APPLICATION, EDR_PASSENGER_ROLES, type PassengerSeedRole } from './edr-passenger.seed';
|
|
|
|
const EDR_ORG_KEY = 'edr';
|
|
const EDR_ORG_NAME = { am: 'EDR', en: 'EDR' };
|
|
const SEED_FLAG = 'SEED_EDR_PASSENGER_ORG';
|
|
|
|
type SeedOrganization = { id: string; key: string };
|
|
|
|
@Injectable()
|
|
export class EdrPassengerOrgSeeder {
|
|
private readonly logger = new Logger(EdrPassengerOrgSeeder.name);
|
|
|
|
constructor(@InjectDataSource() private readonly dataSource: DataSource) {}
|
|
|
|
async run() {
|
|
if (process.env[SEED_FLAG]?.trim().toLowerCase() !== 'true') {
|
|
this.logger.log(`Skipping passenger org seed because ${SEED_FLAG} is not enabled`);
|
|
return;
|
|
}
|
|
|
|
await this.dataSource.transaction(async (manager) => {
|
|
await this.ensureApplication(manager);
|
|
await this.ensurePermissions(manager);
|
|
const organization = await this.ensureOrganization(manager);
|
|
await this.ensureOrganizationConfiguration(manager, organization.id);
|
|
await this.ensureRoles(manager, EDR_PASSENGER_ROLES);
|
|
await this.ensureRolePermissions(manager, EDR_PASSENGER_ROLES);
|
|
await this.ensureSuperAdminPermissions(manager);
|
|
await this.ensurePositions(manager, organization.id);
|
|
});
|
|
|
|
this.logger.log(`Ensured EDR passenger organization seed for '${EDR_ORG_KEY}'`);
|
|
}
|
|
|
|
private async ensureApplication(manager: EntityManager) {
|
|
await manager.getRepository(Application).upsert(
|
|
{
|
|
id: EDR_PASSENGER_APPLICATION.id,
|
|
key: EDR_PASSENGER_APPLICATION.key,
|
|
name: EDR_PASSENGER_APPLICATION.name,
|
|
},
|
|
{ conflictPaths: { key: true } },
|
|
);
|
|
this.logger.log(`Ensured application '${EDR_PASSENGER_APPLICATION.key}'`);
|
|
}
|
|
|
|
private async ensurePermissions(manager: EntityManager) {
|
|
await manager.getRepository(Permission).upsert(
|
|
PASSENGER_PERMISSIONS.map((p) => ({
|
|
id: p.id,
|
|
key: p.key,
|
|
name: p.name,
|
|
applicationId: EDR_PASSENGER_APPLICATION.id,
|
|
})),
|
|
{ conflictPaths: { key: true } },
|
|
);
|
|
this.logger.log(`Ensured ${PASSENGER_PERMISSIONS.length} passenger permissions`);
|
|
}
|
|
|
|
private async ensureOrganization(manager: EntityManager): Promise<SeedOrganization> {
|
|
const repo = manager.getRepository(Organization);
|
|
let org = await repo.findOne({ where: { key: EDR_ORG_KEY }, select: { id: true, key: true } });
|
|
|
|
if (!org) {
|
|
const result = await repo.insert({
|
|
key: EDR_ORG_KEY,
|
|
name: EDR_ORG_NAME,
|
|
isGovernmentOrganization: true,
|
|
});
|
|
this.logger.log(`Seeded EDR passenger organization '${EDR_ORG_KEY}'`);
|
|
return { id: result.identifiers[0]?.id as string, key: EDR_ORG_KEY };
|
|
}
|
|
|
|
this.logger.log(`Ensured EDR passenger organization '${EDR_ORG_KEY}'`);
|
|
return { id: org.id as string, key: EDR_ORG_KEY };
|
|
}
|
|
|
|
private async ensureOrganizationConfiguration(manager: EntityManager, organizationId: string) {
|
|
await manager.getRepository(OrganizationConfiguration).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: PassengerSeedRole[]) {
|
|
await manager.getRepository(Role).upsert(
|
|
seedRoles.map(({ key, name }) => ({ key, name })),
|
|
{ conflictPaths: { key: true } },
|
|
);
|
|
this.logger.log(`Ensured passenger roles: ${seedRoles.map((r) => r.key).join(', ')}`);
|
|
}
|
|
|
|
private async ensureRolePermissions(manager: EntityManager, seedRoles: PassengerSeedRole[]) {
|
|
const allPermissionKeys = [...new Set(seedRoles.flatMap((r) => r.permissionKeys))];
|
|
if (!allPermissionKeys.length) return;
|
|
|
|
const roles = await manager.getRepository(Role).find({
|
|
where: { key: In(seedRoles.map((r) => r.key)) },
|
|
select: { id: true, key: true },
|
|
});
|
|
const permissions = await manager.getRepository(Permission).find({
|
|
where: { key: In(allPermissionKeys) },
|
|
select: { id: true, key: true },
|
|
});
|
|
|
|
const roleByKey = new Map(roles.map((r) => [r.key, r]));
|
|
const permByKey = new Map(permissions.map((p) => [p.key, p]));
|
|
|
|
let totalUpserted = 0;
|
|
let totalPruned = 0;
|
|
|
|
for (const seedRole of seedRoles) {
|
|
const role = roleByKey.get(seedRole.key);
|
|
if (!role) throw new Error(`missing_role:${seedRole.key}`);
|
|
|
|
const desiredPermissionIds = new Set(
|
|
seedRole.permissionKeys.map((key) => {
|
|
const perm = permByKey.get(key);
|
|
if (!perm) throw new Error(`missing_permission:${key}`);
|
|
return perm.id as string;
|
|
}),
|
|
);
|
|
|
|
// Remove links that are no longer in this role's preset
|
|
const existing = await manager.getRepository(RolePermission).find({
|
|
where: { roleId: role.id as string },
|
|
select: { permissionId: true },
|
|
});
|
|
const toRemove = existing
|
|
.map((rp) => rp.permissionId as string)
|
|
.filter((permId) => !desiredPermissionIds.has(permId));
|
|
|
|
if (toRemove.length > 0) {
|
|
await manager.getRepository(RolePermission).delete(
|
|
toRemove.map((permissionId) => ({ roleId: role.id as string, permissionId })),
|
|
);
|
|
totalPruned += toRemove.length;
|
|
}
|
|
|
|
// Upsert the full desired set
|
|
const links = [...desiredPermissionIds].map((permissionId) => ({
|
|
roleId: role.id as string,
|
|
permissionId,
|
|
}));
|
|
await manager.getRepository(RolePermission).upsert(links, {
|
|
conflictPaths: { roleId: true, permissionId: true },
|
|
});
|
|
totalUpserted += links.length;
|
|
}
|
|
|
|
this.logger.log(
|
|
`Synced passenger role-permission links: ${totalUpserted} upserted, ${totalPruned} pruned`,
|
|
);
|
|
}
|
|
|
|
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 super_admin permission links`);
|
|
return;
|
|
}
|
|
|
|
const permissions = await manager.getRepository(Permission).find({
|
|
where: { key: In(PASSENGER_PERMISSION_KEYS) },
|
|
select: { id: true, key: true },
|
|
});
|
|
|
|
if (!permissions.length) return;
|
|
|
|
await manager.getRepository(RolePermission).upsert(
|
|
permissions.map((p) => ({ roleId: role.id, permissionId: p.id })),
|
|
{ conflictPaths: { roleId: true, permissionId: true } },
|
|
);
|
|
this.logger.log(`Ensured ${permissions.length} passenger permissions on super_admin`);
|
|
}
|
|
|
|
private async ensurePositions(manager: EntityManager, organizationId: string) {
|
|
const positions = [
|
|
{ key: 'edr_passenger_director', name: { en: 'Director', am: 'ዳይሬክተር' }, rank: 1 },
|
|
{ key: 'edr_passenger_finance_manager', name: { en: 'Finance Manager', am: 'የፋይናንስ ሥራ አስኪያጅ' }, rank: 2 },
|
|
{ key: 'edr_passenger_finance_officer', name: { en: 'Finance Officer', am: 'የፋይናንስ ኦፊሰር' }, rank: 3 },
|
|
{ key: 'edr_passenger_team_leader', name: { en: 'Team Leader', am: 'ቡድን መሪ' }, rank: 4 },
|
|
{ key: 'edr_passenger_station_master', name: { en: 'Station Master', am: 'ጣቢያ ሃላፊ' }, rank: 5 },
|
|
{ key: 'edr_passenger_station_supervisor', name: { en: 'Station Supervisor', am: 'ጣቢያ ተቆጣጣሪ' }, rank: 6 },
|
|
{ key: 'edr_passenger_ticket_officer', name: { en: 'Passenger Ticket Officer', am: 'የተሳፋሪ ቲኬት ኦፊሰር' }, rank: 7 },
|
|
{ key: 'edr_passenger_operational_staff', name: { en: 'Operational Staff', am: 'ስራ ሰራተኛ' }, rank: 8 },
|
|
];
|
|
|
|
let created = 0;
|
|
for (const pos of positions) {
|
|
const existing = await manager.query<Array<{ id: string }>>(
|
|
`SELECT id FROM iam.positions WHERE key = $1 AND organization_id = $2 LIMIT 1`,
|
|
[pos.key, organizationId],
|
|
);
|
|
if (existing.length === 0) {
|
|
await manager.query(
|
|
`INSERT INTO iam.positions (id, name, key, rank, organization_id, unit_id, position_type_id, created_at, updated_at)
|
|
VALUES (gen_random_uuid(), $1::jsonb, $2, $3, $4::uuid, NULL, NULL, NOW(), NOW())`,
|
|
[JSON.stringify(pos.name), pos.key, pos.rank, organizationId],
|
|
);
|
|
created++;
|
|
}
|
|
}
|
|
this.logger.log(`Ensured ${positions.length} passenger positions (${created} newly created)`);
|
|
}
|
|
}
|