mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 08:26:10 +00:00
fix: bundle integration emails by secret path
This commit is contained in:
@@ -1,7 +1,13 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import { AxiosError } from "axios";
|
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 { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||||
import { Actor, EventType } from "@app/ee/services/audit-log/audit-log-types";
|
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";
|
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");
|
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();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
// If smtp is not configured, we can return early without having to fetch the project members.
|
// 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 projectMembers = await projectMembershipDAL.findAllProjectMembers(projectId);
|
||||||
const project = await projectDAL.findById(projectId);
|
const project = await projectDAL.findById(projectId);
|
||||||
|
|
||||||
const filteredProjectMembers =
|
// 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)
|
||||||
isManual && actorId ? projectMembers.filter((member) => member.userId === actorId) : projectMembers;
|
const filteredProjectMembers = projectMembers
|
||||||
|
.filter((member) => member.roles.some((role) => role.role === ProjectMembershipRole.Admin))
|
||||||
|
.filter((member) => (isManual && actorId ? member.userId === actorId : true));
|
||||||
|
|
||||||
await smtpService.sendMail({
|
await smtpService.sendMail({
|
||||||
recipients: filteredProjectMembers.map((member) => member.user.email!),
|
recipients: filteredProjectMembers.map((member) => member.user.email!),
|
||||||
template: SmtpTemplates.IntegrationSyncFailed,
|
template: SmtpTemplates.IntegrationSyncFailed,
|
||||||
subjectLine: `Integration Sync Failed`,
|
subjectLine: `Integration Sync Failed`,
|
||||||
substitutions: {
|
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,
|
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)
|
({ secretPath: integrationSecPath, isActive }) => isActive && isSamePath(secretPath, integrationSecPath)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const integrationsFailedToSync: { integrationId: string }[] = [];
|
||||||
|
|
||||||
if (!integrations.length) return;
|
if (!integrations.length) return;
|
||||||
logger.info(
|
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}]`
|
`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.
|
// May be undefined, if it's undefined we assume the sync was successful, hence the strict equality type check.
|
||||||
if (response?.isSynced === false) {
|
if (response?.isSynced === false) {
|
||||||
await sendIntegrationSyncFailedMail(integration.id, response?.syncMessage);
|
integrationsFailedToSync.push({
|
||||||
|
integrationId: integration.id
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -893,11 +908,15 @@ export const secretQueueFactory = ({
|
|||||||
isSynced: false
|
isSynced: false
|
||||||
});
|
});
|
||||||
|
|
||||||
await sendIntegrationSyncFailedMail(integration.id, message);
|
integrationsFailedToSync.push({
|
||||||
|
integrationId: integration.id
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await lock.release();
|
await lock.release();
|
||||||
|
|
||||||
|
await sendIntegrationSyncFailedMail(integrationsFailedToSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
await keyStore.setItemWithExpiry(
|
await keyStore.setItemWithExpiry(
|
||||||
|
|||||||
@@ -8,13 +8,20 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h2>Infisical</h2>
|
<h2>Infisical</h2>
|
||||||
<p>An integration in your project
|
|
||||||
<b>{{projectName}}</b>
|
<div>
|
||||||
failed to sync secrets.<br />
|
<p>{{count}} integration(s) failed to sync.</p>
|
||||||
<a href="{{integrationUrl}}">
|
<a href="{{integrationUrl}}">
|
||||||
View integration details.
|
View your project integrations.
|
||||||
</a>
|
</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}}
|
{{#if syncMessage}}
|
||||||
<p><b>Reason: </b>{{syncMessage}}</p>
|
<p><b>Reason: </b>{{syncMessage}}</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user