mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 06:40:57 +00:00
complete rule engine and booking flow
This commit is contained in:
@@ -20,8 +20,9 @@ import { DropdownSettingsModule } from "./modules/dropdown-settings/dropdown-set
|
||||
import { OtpModule } from './modules/otp/otp.module';
|
||||
import { RuleEngineModule } from "./modules/rule-engine/rule-engine.module";
|
||||
import { BackofficeModule } from "./modules/backoffice/backoffice.module";
|
||||
import { DemoPermissionsModule } from "./modules/demo-permissions/demo-permissions.module";
|
||||
import { EdrOrgSeeder } from "./seed/edr-org.seeder";
|
||||
|
||||
import { DemoUsersSeeder } from "./seed/demo-users.seeder";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -49,17 +50,20 @@ import { EdrOrgSeeder } from "./seed/edr-org.seeder";
|
||||
OtpModule,
|
||||
RuleEngineModule,
|
||||
BackofficeModule,
|
||||
DemoPermissionsModule,
|
||||
],
|
||||
providers: [EdrOrgSeeder],
|
||||
providers: [EdrOrgSeeder, DemoUsersSeeder],
|
||||
})
|
||||
export class AppModule implements OnApplicationBootstrap {
|
||||
constructor(
|
||||
private readonly seeder: DataSeeder,
|
||||
private readonly edrOrgSeeder: EdrOrgSeeder,
|
||||
) {}
|
||||
private readonly demoUsersSeeder: DemoUsersSeeder,
|
||||
) { }
|
||||
|
||||
async onApplicationBootstrap() {
|
||||
await this.seeder.run();
|
||||
await this.edrOrgSeeder.run();
|
||||
await this.demoUsersSeeder.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Controller, Get, UseGuards } from "@nestjs/common";
|
||||
import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||
|
||||
import { PermissionGuard } from "@tria-plc/api-common/modules/auth/services/permission.guard";
|
||||
|
||||
@ApiTags("demo-permissions")
|
||||
@Controller()
|
||||
export class DemoPermissionsController {
|
||||
@Get("test_user1")
|
||||
@ApiOperation({ summary: "Permission demo (can:demo:user1)" })
|
||||
@UseGuards(PermissionGuard(["can:demo:user1"]))
|
||||
testUser1() {
|
||||
return { ok: true, permission: "can:demo:user1" };
|
||||
}
|
||||
|
||||
@Get("test_user2")
|
||||
@ApiOperation({ summary: "Permission demo (can:demo:user2)" })
|
||||
@UseGuards(PermissionGuard(["can:demo:user2"]))
|
||||
testUser2() {
|
||||
return { ok: true, permission: "can:demo:user2" };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
import { DemoPermissionsController } from "./demo-permissions.controller";
|
||||
|
||||
@Module({
|
||||
controllers: [DemoPermissionsController],
|
||||
})
|
||||
export class DemoPermissionsModule {}
|
||||
213
apps/edr-freight-api/src/seed/demo-users.seeder.ts
Normal file
213
apps/edr-freight-api/src/seed/demo-users.seeder.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
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)",
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user