mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-03 16:26:01 +00:00
feat(notifications): sync failed notification + ui tweaks
This commit is contained in:
@@ -1148,7 +1148,8 @@ export const registerRoutes = async (
|
|||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
gatewayV2Service
|
gatewayV2Service,
|
||||||
|
notificationService
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretQueueService = secretQueueFactory({
|
const secretQueueService = secretQueueFactory({
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ export enum NotificationType {
|
|||||||
IMPORT_FAILED = "import-failed",
|
IMPORT_FAILED = "import-failed",
|
||||||
DIRECT_PROJECT_ACCESS_ISSUED_TO_ADMIN = "direct-project-access-issued-to-admin",
|
DIRECT_PROJECT_ACCESS_ISSUED_TO_ADMIN = "direct-project-access-issued-to-admin",
|
||||||
PROJECT_ACCESS_REQUEST = "project-access-request",
|
PROJECT_ACCESS_REQUEST = "project-access-request",
|
||||||
PROJECT_INVITATION = "project-invitation"
|
PROJECT_INVITATION = "project-invitation",
|
||||||
|
SECRET_SYNC_FAILED = "secret-sync-failed"
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TCreateUserNotificationDTO {
|
export interface TCreateUserNotificationDTO {
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
|||||||
|
|
||||||
import { TAppConnectionDALFactory } from "../app-connection/app-connection-dal";
|
import { TAppConnectionDALFactory } from "../app-connection/app-connection-dal";
|
||||||
import { TFolderCommitServiceFactory } from "../folder-commit/folder-commit-service";
|
import { TFolderCommitServiceFactory } from "../folder-commit/folder-commit-service";
|
||||||
|
import { TNotificationServiceFactory } from "../notification/notification-service";
|
||||||
|
import { NotificationType } from "../notification/notification-types";
|
||||||
|
|
||||||
export type TSecretSyncQueueFactory = ReturnType<typeof secretSyncQueueFactory>;
|
export type TSecretSyncQueueFactory = ReturnType<typeof secretSyncQueueFactory>;
|
||||||
|
|
||||||
@@ -100,6 +102,7 @@ type TSecretSyncQueueFactoryDep = {
|
|||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
||||||
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">;
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">;
|
||||||
|
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SecretSyncActionJob = Job<
|
type SecretSyncActionJob = Job<
|
||||||
@@ -142,7 +145,8 @@ export const secretSyncQueueFactory = ({
|
|||||||
folderCommitService,
|
folderCommitService,
|
||||||
licenseService,
|
licenseService,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
gatewayV2Service
|
gatewayV2Service,
|
||||||
|
notificationService
|
||||||
}: TSecretSyncQueueFactoryDep) => {
|
}: TSecretSyncQueueFactoryDep) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
@@ -898,6 +902,19 @@ export const secretSyncQueueFactory = ({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const syncPath = `/projects/secret-management/${projectId}/integrations/secret-syncs/${destination}/${secretSync.id}`;
|
||||||
|
|
||||||
|
await notificationService.createUserNotifications(
|
||||||
|
projectAdmins.map((admin) => ({
|
||||||
|
userId: admin.userId,
|
||||||
|
orgId: project.orgId,
|
||||||
|
type: NotificationType.SECRET_SYNC_FAILED,
|
||||||
|
title: `Secret Sync Failed to ${actionLabel} Secrets`,
|
||||||
|
body: `Your **${syncDestination}** sync **${name}** failed to complete${failureMessage ? `: \`${failureMessage}\`` : ""}`,
|
||||||
|
link: syncPath
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
await smtpService.sendMail({
|
await smtpService.sendMail({
|
||||||
recipients: projectAdmins.map((member) => member.user.email!).filter(Boolean),
|
recipients: projectAdmins.map((member) => member.user.email!).filter(Boolean),
|
||||||
template: SmtpTemplates.SecretSyncFailed,
|
template: SmtpTemplates.SecretSyncFailed,
|
||||||
@@ -910,7 +927,7 @@ export const secretSyncQueueFactory = ({
|
|||||||
secretPath: folder?.path,
|
secretPath: folder?.path,
|
||||||
environment: environment?.name,
|
environment: environment?.name,
|
||||||
projectName: project.name,
|
projectName: project.name,
|
||||||
syncUrl: `${appCfg.SITE_URL}/projects/secret-management/${projectId}/integrations/secret-syncs/${destination}/${secretSync.id}`
|
syncUrl: `${appCfg.SITE_URL}${syncPath}`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ export const Notification = ({ notification, onDelete }: Props) => {
|
|||||||
{!notification.isRead && (
|
{!notification.isRead && (
|
||||||
<FontAwesomeIcon icon={faCircle} className="mt-1.5 size-2 text-yellow-400" />
|
<FontAwesomeIcon icon={faCircle} className="mt-1.5 size-2 text-yellow-400" />
|
||||||
)}
|
)}
|
||||||
<Tooltip content={<Markdown>{notification.title}</Markdown>} delayDuration={300}>
|
<Tooltip
|
||||||
|
content={<Markdown>{notification.title}</Markdown>}
|
||||||
|
delayDuration={300}
|
||||||
|
className="z-[1000]"
|
||||||
|
>
|
||||||
<span className="overflow-hidden text-ellipsis whitespace-nowrap text-sm font-medium leading-5 text-mineshaft-100">
|
<span className="overflow-hidden text-ellipsis whitespace-nowrap text-sm font-medium leading-5 text-mineshaft-100">
|
||||||
<Markdown components={{ p: "span" }}>{notification.title}</Markdown>
|
<Markdown components={{ p: "span" }}>{notification.title}</Markdown>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const NotificationDropdown = () => {
|
|||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
align="end"
|
align="end"
|
||||||
side="bottom"
|
side="bottom"
|
||||||
className="z-[9999] mt-3 flex h-[550px] w-[400px] overflow-hidden rounded-lg"
|
className="z-[999] mt-3 flex h-[550px] w-[400px] overflow-hidden rounded-lg"
|
||||||
>
|
>
|
||||||
<div className="flex w-full flex-col">
|
<div className="flex w-full flex-col">
|
||||||
<div className="flex items-center justify-between border-b border-mineshaft-500 px-3 py-2">
|
<div className="flex items-center justify-between border-b border-mineshaft-500 px-3 py-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user