mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
fix: bundle integration emails by secret path
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
import { ProjectUpgradeStatus, ProjectVersion, TSecretSnapshotSecretsV2, TSecretVersionsV2 } from "@app/db/schemas";
|
||||
import {
|
||||
ProjectMembershipRole,
|
||||
ProjectUpgradeStatus,
|
||||
ProjectVersion,
|
||||
TSecretSnapshotSecretsV2,
|
||||
TSecretVersionsV2
|
||||
} from "@app/db/schemas";
|
||||
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||
import { Actor, EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal";
|
||||
@@ -562,7 +568,7 @@ export const secretQueueFactory = ({
|
||||
throw new Error("Secret path not found");
|
||||
}
|
||||
|
||||
const sendIntegrationSyncFailedMail = async (integrationId: string, syncMessage: string | null) => {
|
||||
const sendIntegrationSyncFailedMail = async (integrations: { integrationId: string; syncMessage?: string }[]) => {
|
||||
const appCfg = getConfig();
|
||||
|
||||
// If smtp is not configured, we can return early without having to fetch the project members.
|
||||
@@ -571,17 +577,22 @@ export const secretQueueFactory = ({
|
||||
const projectMembers = await projectMembershipDAL.findAllProjectMembers(projectId);
|
||||
const project = await projectDAL.findById(projectId);
|
||||
|
||||
const filteredProjectMembers =
|
||||
isManual && actorId ? projectMembers.filter((member) => member.userId === actorId) : projectMembers;
|
||||
// Only send emails to admins, and if its a manual trigger, only send it to the person who triggered it (if actor is admin as well)
|
||||
const filteredProjectMembers = projectMembers
|
||||
.filter((member) => member.roles.some((role) => role.role === ProjectMembershipRole.Admin))
|
||||
.filter((member) => (isManual && actorId ? member.userId === actorId : true));
|
||||
|
||||
await smtpService.sendMail({
|
||||
recipients: filteredProjectMembers.map((member) => member.user.email!),
|
||||
template: SmtpTemplates.IntegrationSyncFailed,
|
||||
subjectLine: `Integration Sync Failed`,
|
||||
substitutions: {
|
||||
syncMessage,
|
||||
syncMessage: integrations[0]?.syncMessage, // We are only displaying the sync message if its a singular integration, so we can just grab the first one in the array.
|
||||
secretPath,
|
||||
environment: folder.environment.name,
|
||||
count: integrations.length,
|
||||
projectName: project.name,
|
||||
integrationUrl: `${appCfg.SITE_URL}/integrations/details/${integrationId}`
|
||||
integrationUrl: `${appCfg.SITE_URL}/integrations/${project.id}`
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -687,6 +698,8 @@ export const secretQueueFactory = ({
|
||||
({ secretPath: integrationSecPath, isActive }) => isActive && isSamePath(secretPath, integrationSecPath)
|
||||
);
|
||||
|
||||
const integrationsFailedToSync: { integrationId: string }[] = [];
|
||||
|
||||
if (!integrations.length) return;
|
||||
logger.info(
|
||||
`getIntegrationSecrets: secret integration sync started [jobId=${job.id}] [jobId=${job.id}] [projectId=${job.data.projectId}] [environment=${job.data.environment}] [secretPath=${job.data.secretPath}] [depth=${job.data.depth}]`
|
||||
@@ -860,7 +873,9 @@ export const secretQueueFactory = ({
|
||||
|
||||
// May be undefined, if it's undefined we assume the sync was successful, hence the strict equality type check.
|
||||
if (response?.isSynced === false) {
|
||||
await sendIntegrationSyncFailedMail(integration.id, response?.syncMessage);
|
||||
integrationsFailedToSync.push({
|
||||
integrationId: integration.id
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
@@ -893,11 +908,15 @@ export const secretQueueFactory = ({
|
||||
isSynced: false
|
||||
});
|
||||
|
||||
await sendIntegrationSyncFailedMail(integration.id, message);
|
||||
integrationsFailedToSync.push({
|
||||
integrationId: integration.id
|
||||
});
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await lock.release();
|
||||
|
||||
await sendIntegrationSyncFailedMail(integrationsFailedToSync);
|
||||
}
|
||||
|
||||
await keyStore.setItemWithExpiry(
|
||||
|
||||
@@ -8,13 +8,20 @@
|
||||
|
||||
<body>
|
||||
<h2>Infisical</h2>
|
||||
<p>An integration in your project
|
||||
<b>{{projectName}}</b>
|
||||
failed to sync secrets.<br />
|
||||
|
||||
<div>
|
||||
<p>{{count}} integration(s) failed to sync.</p>
|
||||
<a href="{{integrationUrl}}">
|
||||
View integration details.
|
||||
View your project integrations.
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<div>
|
||||
<p><strong>Project</strong>: {{projectName}}</p>
|
||||
<p><strong>Environment</strong>: {{environment}}</p>
|
||||
<p><strong>Secret Path</strong>: {{secretPath}}</p>
|
||||
</div>
|
||||
|
||||
{{#if syncMessage}}
|
||||
<p><b>Reason: </b>{{syncMessage}}</p>
|
||||
|
||||
Reference in New Issue
Block a user