feat(app): wire dashboard administration flows

This commit is contained in:
2026-05-07 15:35:37 +00:00
parent e9eb9b4172
commit 91f06ccae1
91 changed files with 4087 additions and 5863 deletions
+35 -6
View File
@@ -59,6 +59,10 @@ export class UserInvitationManager {
async (requestArg) => {
const user = await this.receptionRef.userManager.getUserByJwtValidation(requestArg.jwt);
await this.verifyUserIsAdminOfOrg(user.id, requestArg.organizationId);
const roles = await this.receptionRef.organizationmanager.assertRoleKeysAreValid(
requestArg.organizationId,
requestArg.roles
);
const email = requestArg.email.toLowerCase().trim();
@@ -86,7 +90,7 @@ export class UserInvitationManager {
action: 'create',
userId: existingUser.id,
organizationId: requestArg.organizationId,
roles: requestArg.roles,
roles,
});
return {
success: true,
@@ -103,14 +107,14 @@ export class UserInvitationManager {
let isNew = false;
if (invitation) {
// Add org to existing invitation
await invitation.addOrganization(requestArg.organizationId, user.id, requestArg.roles);
await invitation.addOrganization(requestArg.organizationId, user.id, roles);
} else {
// Create new invitation
invitation = await UserInvitation.createNewInvitation(
email,
requestArg.organizationId,
user.id,
requestArg.roles
roles
);
isNew = true;
}
@@ -323,6 +327,10 @@ export class UserInvitationManager {
async (requestArg) => {
const user = await this.receptionRef.userManager.getUserByJwtValidation(requestArg.jwt);
await this.verifyUserIsAdminOfOrg(user.id, requestArg.organizationId);
const roles = await this.receptionRef.organizationmanager.assertRoleKeysAreValid(
requestArg.organizationId,
requestArg.roles
);
const role = await this.receptionRef.roleManager.CRole.getInstance({
data: {
@@ -336,7 +344,7 @@ export class UserInvitationManager {
}
// If removing owner role, check we're not removing the last owner
if (role.data.roles.includes('owner') && !requestArg.roles.includes('owner')) {
if (role.data.roles.includes('owner') && !roles.includes('owner')) {
const allRoles = await this.receptionRef.roleManager.CRole.getInstances({
data: { organizationId: requestArg.organizationId },
});
@@ -349,7 +357,7 @@ export class UserInvitationManager {
}
}
role.data.roles = requestArg.roles;
role.data.roles = roles;
await role.save();
const updatedUser = await this.receptionRef.userManager.CUser.getInstance({
@@ -360,7 +368,7 @@ export class UserInvitationManager {
eventType: 'org_member_roles_updated',
severity: 'high',
title: 'Organization member roles updated',
body: `${user.data.email} changed roles for ${updatedUser?.data?.email || requestArg.userId} to ${requestArg.roles.join(', ')}.`,
body: `${user.data.email} changed roles for ${updatedUser?.data?.email || requestArg.userId} to ${roles.join(', ')}.`,
actorUserId: user.id,
relatedEntityId: requestArg.userId,
relatedEntityType: 'user',
@@ -391,6 +399,18 @@ export class UserInvitationManager {
);
}
const organization = await this.receptionRef.organizationmanager.COrganization.getInstance({
id: requestArg.organizationId,
});
if (!organization) {
throw new plugins.typedrequest.TypedResponseError('Organization not found.');
}
if ((requestArg.confirmationText || '').trim() !== `transfer ${organization.data.slug}`) {
throw new plugins.typedrequest.TypedResponseError(
`Confirmation text must be exactly "transfer ${organization.data.slug}".`
);
}
// Get new owner's role
const newOwnerRole = await this.receptionRef.roleManager.CRole.getInstance({
data: {
@@ -418,6 +438,15 @@ export class UserInvitationManager {
const newOwner = await this.receptionRef.userManager.CUser.getInstance({
id: requestArg.newOwnerId,
});
await this.receptionRef.activityLogManager.logActivity(
user.id,
'org_ownership_transferred',
`${user.data.email} transferred ownership of ${organization.data.name} to ${newOwner?.data?.email || requestArg.newOwnerId}.`,
{
targetId: requestArg.organizationId,
targetType: 'organization',
}
);
await this.emitOrganizationAlert({
organizationId: requestArg.organizationId,
eventType: 'org_ownership_transferred',