208 lines
7.4 KiB
TypeScript
208 lines
7.4 KiB
TypeScript
import { DefaultAdminRoles } from "~/application/dtos/shared/DefaultAdminRoles";
|
|
import { DefaultAppRoles } from "~/application/dtos/shared/DefaultAppRoles";
|
|
import { DefaultPermission } from "~/application/dtos/shared/DefaultPermissions";
|
|
import {
|
|
defaultAdminRoleSeeds,
|
|
defaultAppRoleSeeds,
|
|
defaultPermissionSeedDescriptors,
|
|
syncDefaultRolesAndPermissions,
|
|
type DefaultPermissionSeed,
|
|
type DefaultRoleSeed,
|
|
} from "@toolctrl/db/permissions-seed.server";
|
|
import { getAllEntities } from "~/utils/db/entities/entities.db.server";
|
|
import { getAllRoles } from "~/utils/db/permissions/roles.db.server";
|
|
import { createUserRoleAssignments, type UserRoleAssignment } from "~/utils/db/permissions/userRoles.db.server";
|
|
import { adminGetAllTenants } from "~/utils/db/tenants.db.server";
|
|
import { adminGetAllUsers } from "~/utils/db/users.db.server";
|
|
import { db } from "~/utils/db.server";
|
|
import { clearCacheKey } from "~/utils/cache.server";
|
|
import { getEntityPermissions } from "~/utils/helpers/PermissionsHelper";
|
|
|
|
export type CreateRoleDto = DefaultRoleSeed;
|
|
export const defaultAdminRoles: CreateRoleDto[] = [...defaultAdminRoleSeeds];
|
|
export const defaultAppRoles: CreateRoleDto[] = [...defaultAppRoleSeeds];
|
|
|
|
export type CreatePermissionDto = Omit<DefaultPermissionSeed, "name" | "type"> & {
|
|
name: DefaultPermission;
|
|
type: string;
|
|
};
|
|
export const defaultPermissions: CreatePermissionDto[] = defaultPermissionSeedDescriptors.map((permission) => ({
|
|
...permission,
|
|
name: permission.name as DefaultPermission,
|
|
}));
|
|
|
|
export type RolesAndPermissionsSeedDiagnosticPhase = "start" | "success" | "failure";
|
|
|
|
export type RolesAndPermissionsSeedDiagnosticEvent = {
|
|
phase: RolesAndPermissionsSeedDiagnosticPhase;
|
|
adminRoleCount?: number;
|
|
appRoleCount?: number;
|
|
defaultPermissionCount?: number;
|
|
dynamicEntityPermissionCount?: number;
|
|
adminUserAssignmentCount?: number;
|
|
tenantUserAssignmentCount?: number;
|
|
error?: unknown;
|
|
};
|
|
|
|
export type RolesAndPermissionsSeedDiagnostics = (event: RolesAndPermissionsSeedDiagnosticEvent) => void | Promise<void>;
|
|
|
|
export type SeedRolesAndPermissionsOptions = {
|
|
adminEmail?: string;
|
|
diagnostics?: RolesAndPermissionsSeedDiagnostics;
|
|
};
|
|
|
|
function toSeedRolesAndPermissionsOptions(adminEmailOrOptions?: string | SeedRolesAndPermissionsOptions): SeedRolesAndPermissionsOptions {
|
|
if (typeof adminEmailOrOptions === "string") {
|
|
return { adminEmail: adminEmailOrOptions };
|
|
}
|
|
return adminEmailOrOptions ?? {};
|
|
}
|
|
|
|
async function emitRolesAndPermissionsSeedDiagnostic(
|
|
diagnostics: RolesAndPermissionsSeedDiagnostics | undefined,
|
|
event: RolesAndPermissionsSeedDiagnosticEvent
|
|
): Promise<void> {
|
|
await diagnostics?.(event);
|
|
}
|
|
|
|
async function createDynamicEntityPermissions(permissions: { inRoles: string[]; name: string; description: string; type: string; order: number }[]): Promise<void> {
|
|
if (permissions.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const permissionNames = [...new Set(permissions.map((permission) => permission.name))];
|
|
await db.permission.createMany({
|
|
data: permissions.map((permission) => ({
|
|
order: permission.order,
|
|
name: permission.name,
|
|
description: permission.description,
|
|
type: permission.type,
|
|
isDefault: true,
|
|
entityId: null,
|
|
})),
|
|
skipDuplicates: true,
|
|
});
|
|
permissionNames.forEach((permissionName) => {
|
|
clearCacheKey(`permission:${permissionName}`);
|
|
});
|
|
|
|
const createdOrExistingPermissions = await db.permission.findMany({
|
|
where: {
|
|
name: {
|
|
in: permissionNames,
|
|
},
|
|
},
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
},
|
|
});
|
|
const permissionByName = new Map(createdOrExistingPermissions.map((permission) => [permission.name, permission]));
|
|
const roleNames = [...new Set(permissions.flatMap((permission) => permission.inRoles))];
|
|
const roles = await db.role.findMany({
|
|
where: {
|
|
name: {
|
|
in: roleNames,
|
|
},
|
|
tenantId: null,
|
|
},
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
},
|
|
});
|
|
const roleByName = new Map(roles.map((role) => [role.name, role]));
|
|
const rolePermissionData = permissions.flatMap((permission) => {
|
|
const createdOrExistingPermission = permissionByName.get(permission.name);
|
|
if (!createdOrExistingPermission) {
|
|
return [];
|
|
}
|
|
return permission.inRoles.map((roleName) => {
|
|
const role = roleByName.get(roleName);
|
|
if (!role) {
|
|
throw new Error("Role required: " + roleName);
|
|
}
|
|
return {
|
|
permissionId: createdOrExistingPermission.id,
|
|
roleId: role.id,
|
|
};
|
|
});
|
|
});
|
|
|
|
if (rolePermissionData.length > 0) {
|
|
await db.rolePermission.createMany({
|
|
data: rolePermissionData,
|
|
skipDuplicates: true,
|
|
});
|
|
}
|
|
}
|
|
|
|
export async function seedRolesAndPermissions(adminEmailOrOptions?: string | SeedRolesAndPermissionsOptions): Promise<void> {
|
|
const { adminEmail, diagnostics } = toSeedRolesAndPermissionsOptions(adminEmailOrOptions);
|
|
const counts = {
|
|
adminRoleCount: defaultAdminRoles.length,
|
|
appRoleCount: defaultAppRoles.length,
|
|
defaultPermissionCount: defaultPermissions.length,
|
|
dynamicEntityPermissionCount: 0,
|
|
adminUserAssignmentCount: 0,
|
|
tenantUserAssignmentCount: 0,
|
|
};
|
|
|
|
await emitRolesAndPermissionsSeedDiagnostic(diagnostics, { phase: "start", ...counts });
|
|
|
|
try {
|
|
await syncDefaultRolesAndPermissions({ client: db });
|
|
const adminRoles = await getAllRoles("admin");
|
|
counts.adminRoleCount = adminRoles.length;
|
|
|
|
const appRoles = await getAllRoles("app");
|
|
counts.appRoleCount = appRoles.length;
|
|
|
|
const entities = await getAllEntities({ tenantId: null, active: true });
|
|
const entityPermissionGroups = await Promise.all(
|
|
entities.map(async (entity) =>
|
|
(await getEntityPermissions(entity)).map((permission) => ({
|
|
inRoles: [DefaultAdminRoles.SuperAdmin, DefaultAppRoles.SuperUser, DefaultAppRoles.Admin, DefaultAppRoles.User].map((f) => f.toString()),
|
|
name: permission.name as DefaultPermission,
|
|
description: permission.description,
|
|
type: entity.type,
|
|
}))
|
|
)
|
|
);
|
|
const appPermissionCount = defaultPermissions.filter((f) => f.type === "app").length;
|
|
const dynamicEntityPermissions = entityPermissionGroups.flat().map((permission, index) => ({
|
|
...permission,
|
|
order: appPermissionCount + index + 3,
|
|
}));
|
|
counts.dynamicEntityPermissionCount = dynamicEntityPermissions.length;
|
|
await createDynamicEntityPermissions(dynamicEntityPermissions);
|
|
|
|
const users = (await adminGetAllUsers()).items;
|
|
const userRoleAssignments: UserRoleAssignment[] = [];
|
|
users
|
|
.filter((f) => f.admin && adminEmail && f.email === adminEmail)
|
|
.forEach((user) => {
|
|
adminRoles.forEach((adminRole) => {
|
|
counts.adminUserAssignmentCount += 1;
|
|
userRoleAssignments.push({ userId: user.id, roleId: adminRole.id, tenantId: null });
|
|
});
|
|
});
|
|
|
|
const tenants = await adminGetAllTenants();
|
|
tenants.forEach((tenant) => {
|
|
tenant.users.forEach((tenantUser) => {
|
|
appRoles.forEach((appRole) => {
|
|
counts.tenantUserAssignmentCount += 1;
|
|
userRoleAssignments.push({ userId: tenantUser.userId, roleId: appRole.id, tenantId: tenant.id });
|
|
});
|
|
});
|
|
});
|
|
await createUserRoleAssignments(userRoleAssignments);
|
|
|
|
await emitRolesAndPermissionsSeedDiagnostic(diagnostics, { phase: "success", ...counts });
|
|
} catch (error) {
|
|
await emitRolesAndPermissionsSeedDiagnostic(diagnostics, { phase: "failure", ...counts, error });
|
|
throw error;
|
|
}
|
|
}
|