import { Injectable, Logger } from "@nestjs/common"; import { hashPassword } from "@tria-plc/api-common/utils/argon"; import { EUserStatus } from "@tria-plc/api-common/utils/enums/user.enum"; import { ERoleKey } from "@tria-plc/api-common/utils/enums/seed.enum"; import { Employee, Organization, Permission, Role, RolePermission, User, UserCredential, UserRole, } from "@tria-plc/iamapi-common"; import { DataSource } from "typeorm"; const SEED_FLAG = "SEED_DEMO_USERS"; const DEMO_ORG_KEY = "demo_iam"; const DEMO_ORG_NAME = { en: "Demo IAM" }; const DEMO_PERMISSIONS = [ { key: "can:demo:user1", name: { en: "Can access demo user1" } }, { key: "can:demo:user2", name: { en: "Can access demo user2" } }, ]; const DEMO_ROLES = [ { key: "demo_user1", name: { en: "Demo User1" } }, { key: "demo_user2", name: { en: "Demo User2" } }, ]; const DEMO_USERS = [ { email: "user@gmail.com", username: "user", name: { en: "Demo User 1" }, roleKey: "demo_user1", }, { email: "user2@gmail.com", username: "user2", name: { en: "Demo User 2" }, roleKey: "demo_user2", }, ]; @Injectable() export class DemoUsersSeeder { private readonly logger = new Logger(DemoUsersSeeder.name); constructor(private readonly dataSource: DataSource) {} async run() { const shouldSeed = process.env[SEED_FLAG]?.trim().toLowerCase() === "true"; if (!shouldSeed) { this.logger.log(`Skipping demo user seed because ${SEED_FLAG} is not enabled`); return; } await this.dataSource.transaction(async (manager) => { const organizationRepository = manager.getRepository(Organization); const employeeRepository = manager.getRepository(Employee); const permissionRepository = manager.getRepository(Permission); const roleRepository = manager.getRepository(Role); const rolePermissionRepository = manager.getRepository(RolePermission); const userRepository = manager.getRepository(User); const userCredentialRepository = manager.getRepository(UserCredential); const userRoleRepository = manager.getRepository(UserRole); await organizationRepository.upsert( { key: DEMO_ORG_KEY, name: DEMO_ORG_NAME, // status defaults to ACTIVE in IAM entity isGovernmentOrganization: true, }, { conflictPaths: { key: true } }, ); const organization = await organizationRepository.findOne({ where: { key: DEMO_ORG_KEY }, select: { id: true, key: true }, }); if (!organization) { throw new Error("demo_org_seed_failed"); } await permissionRepository.upsert(DEMO_PERMISSIONS, { conflictPaths: { key: true }, }); await roleRepository.upsert(DEMO_ROLES, { conflictPaths: { key: true }, }); const roles = await roleRepository.find({ where: DEMO_ROLES.map((r) => ({ key: r.key })) }); const permissions = await permissionRepository.find({ where: DEMO_PERMISSIONS.map((p) => ({ key: p.key })), }); const roleByKey = new Map(roles.map((r) => [r.key, r])); const permissionByKey = new Map(permissions.map((p) => [p.key, p])); const superAdminRole = await roleRepository.findOne({ where: { key: ERoleKey.SUPER_ADMIN }, select: { id: true, key: true }, }); const rolePermissionsToUpsert = [ { roleId: roleByKey.get("demo_user1")!.id, permissionId: permissionByKey.get("can:demo:user1")!.id, }, { roleId: roleByKey.get("demo_user2")!.id, permissionId: permissionByKey.get("can:demo:user2")!.id, }, ...(superAdminRole ? ([ { roleId: superAdminRole.id, permissionId: permissionByKey.get("can:demo:user1")!.id, }, { roleId: superAdminRole.id, permissionId: permissionByKey.get("can:demo:user2")!.id, }, ] as Array<{ roleId: string; permissionId: string }>) : []), ]; await rolePermissionRepository.upsert(rolePermissionsToUpsert, { conflictPaths: { roleId: true, permissionId: true }, }); const hashedPassword = await hashPassword("12345678"); for (const demoUser of DEMO_USERS) { const existingUser = await userRepository.findOne({ where: { email: demoUser.email }, select: { id: true, email: true }, }); let user = existingUser; if (!user) { user = await userRepository.save( userRepository.create({ email: demoUser.email, username: demoUser.username, name: demoUser.name, isActive: true, hasSetPassword: true, status: EUserStatus.ACCEPTED, }), ); } // Ensure an active credential exists for login. const activeCredentialExists = await userCredentialRepository.exists({ where: { userId: user.id, isActive: true, }, }); if (!activeCredentialExists) { await userCredentialRepository.insert({ userId: user.id, password: hashedPassword, isActive: true, }); } // Login query requires a current employee in an ACTIVE organization. const employeeExists = await employeeRepository.exists({ where: { userId: user.id, organizationId: organization.id, isCurrent: true, }, }); if (!employeeExists) { await employeeRepository.insert({ userId: user.id, organizationId: organization.id, isCurrent: true, name: demoUser.name, }); } const role = roleByKey.get(demoUser.roleKey); if (!role) { throw new Error(`missing_role:${demoUser.roleKey}`); } await userRoleRepository.upsert( { userId: user.id, roleId: role.id, organizationId: organization.id, }, { conflictPaths: { userId: true, roleId: true } }, ); } }); this.logger.log( "Seeded demo users + permissions (user@gmail.com, user2@gmail.com; permissions can:demo:user1/can:demo:user2)", ); } }