diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index eff862f78..c0e7827c2 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -888,7 +888,8 @@ export const registerRoutes = async ( projectDAL, permissionService, projectUserMembershipRoleDAL, - projectMembershipDAL + projectMembershipDAL, + notificationService }); const rateLimitService = rateLimitServiceFactory({ diff --git a/backend/src/services/notification/notification-types.ts b/backend/src/services/notification/notification-types.ts index 5743118a6..0d0cf1f15 100644 --- a/backend/src/services/notification/notification-types.ts +++ b/backend/src/services/notification/notification-types.ts @@ -11,7 +11,8 @@ export enum NotificationType { ADMIN_SSO_BYPASS = "admin-sso-bypass", IMPORT_STARTED = "import-started", IMPORT_SUCCESSFUL = "import-successful", - IMPORT_FAILED = "import-failed" + IMPORT_FAILED = "import-failed", + DIRECT_PROJECT_ACCESS_ISSUED_TO_ADMIN = "direct-project-access-issued-to-admin" } export interface TCreateUserNotificationDTO { diff --git a/backend/src/services/org-admin/org-admin-service.ts b/backend/src/services/org-admin/org-admin-service.ts index 23005e52b..9c8b574a8 100644 --- a/backend/src/services/org-admin/org-admin-service.ts +++ b/backend/src/services/org-admin/org-admin-service.ts @@ -5,6 +5,8 @@ import { OrgPermissionAdminConsoleAction, OrgPermissionSubjects } from "@app/ee/ import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; +import { TNotificationServiceFactory } from "../notification/notification-service"; +import { NotificationType } from "../notification/notification-types"; import { TProjectDALFactory } from "../project/project-dal"; import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; @@ -20,6 +22,7 @@ type TOrgAdminServiceFactoryDep = { >; projectUserMembershipRoleDAL: Pick; smtpService: Pick; + notificationService: Pick; }; export type TOrgAdminServiceFactory = ReturnType; @@ -29,7 +32,8 @@ export const orgAdminServiceFactory = ({ projectDAL, projectMembershipDAL, projectUserMembershipRoleDAL, - smtpService + smtpService, + notificationService }: TOrgAdminServiceFactoryDep) => { const listOrgProjects = async ({ actor, @@ -137,16 +141,35 @@ export const orgAdminServiceFactory = ({ .map((el) => el.user.email!) .filter(Boolean); - if (filteredProjectMembers.length) { - await smtpService.sendMail({ - template: SmtpTemplates.OrgAdminProjectDirectAccess, - recipients: filteredProjectMembers, - subjectLine: "Organization Admin Project Direct Access Issued", - substitutions: { - projectName: project.name, - email: projectMembers.find((el) => el.userId === actorId)?.user?.username - } - }); + const actorEmail = projectMembers.find((el) => el.userId === actorId)?.user?.username; + + if (actorEmail) { + await notificationService.createUserNotifications( + projectMembers + .filter( + (member) => + member.roles.some((role) => role.role === ProjectMembershipRole.Admin) && member.userId !== actorId + ) + .map((member) => ({ + userId: member.userId, + orgId: project.orgId, + type: NotificationType.DIRECT_PROJECT_ACCESS_ISSUED_TO_ADMIN, + title: "Direct Project Access Issued", + body: `The organization admin **${actorEmail}** has self-issued direct access to the project **${project.name}**.` + })) + ); + + if (filteredProjectMembers.length) { + await smtpService.sendMail({ + template: SmtpTemplates.OrgAdminProjectDirectAccess, + recipients: filteredProjectMembers, + subjectLine: "Organization Admin Project Direct Access Issued", + substitutions: { + projectName: project.name, + email: actorEmail + } + }); + } } return { isExistingMember: false, membership: updatedMembership }; };