feat: webhook on secret reminder trigger

This commit is contained in:
=
2025-03-07 15:01:14 +05:30
parent 706feafbf2
commit 6cd448b8a5
6 changed files with 158 additions and 46 deletions
+81 -32
View File
@@ -11,7 +11,7 @@ import { logger } from "@app/lib/logger";
import { TProjectDALFactory } from "../project/project-dal";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TWebhookDALFactory } from "./webhook-dal";
import { WebhookType } from "./webhook-types";
import { TWebhookPayloads, WebhookEvents, WebhookType } from "./webhook-types";
const WEBHOOK_TRIGGER_TIMEOUT = 15 * 1000;
@@ -54,29 +54,64 @@ export const triggerWebhookRequest = async (
return req;
};
export const getWebhookPayload = (
eventName: string,
details: {
workspaceName: string;
workspaceId: string;
environment: string;
secretPath?: string;
type?: string | null;
export const getWebhookPayload = (event: TWebhookPayloads) => {
if (event.type === WebhookEvents.SecretModified) {
const { projectName, projectId, environment, secretPath, type } = event.payload;
switch (type) {
case WebhookType.SLACK:
return {
text: "A secret value has been added or modified.",
attachments: [
{
color: "#E7F256",
fields: [
{
title: "Project",
value: projectName,
short: false
},
{
title: "Environment",
value: environment,
short: false
},
{
title: "Secret Path",
value: secretPath,
short: false
}
]
}
]
};
case WebhookType.GENERAL:
default:
return {
event: event.type,
project: {
workspaceId: projectId,
projectName,
environment,
secretPath
}
};
}
}
) => {
const { workspaceName, workspaceId, environment, secretPath, type } = details;
const { projectName, projectId, environment, secretPath, type, reminderNote, secretName } = event.payload;
switch (type) {
case WebhookType.SLACK:
return {
text: "A secret value has been added or modified.",
text: "You have a secret reminder",
attachments: [
{
color: "#E7F256",
fields: [
{
title: "Project",
value: workspaceName,
value: projectName,
short: false
},
{
@@ -88,6 +123,16 @@ export const getWebhookPayload = (
title: "Secret Path",
value: secretPath,
short: false
},
{
title: "Secret Name",
value: secretName,
short: false
},
{
title: "Reminder Note",
value: reminderNote,
short: false
}
]
}
@@ -96,11 +141,14 @@ export const getWebhookPayload = (
case WebhookType.GENERAL:
default:
return {
event: eventName,
event: event.type,
project: {
workspaceId,
workspaceId: projectId,
projectName,
environment,
secretPath
secretPath,
secretName,
reminderNote
}
};
}
@@ -110,6 +158,7 @@ export type TFnTriggerWebhookDTO = {
projectId: string;
secretPath: string;
environment: string;
event: TWebhookPayloads;
webhookDAL: Pick<TWebhookDALFactory, "findAllWebhooks" | "transaction" | "update" | "bulkUpdate">;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
projectDAL: Pick<TProjectDALFactory, "findById">;
@@ -124,8 +173,9 @@ export const fnTriggerWebhook = async ({
projectId,
webhookDAL,
projectEnvDAL,
projectDAL,
secretManagerDecryptor
event,
secretManagerDecryptor,
projectDAL
}: TFnTriggerWebhookDTO) => {
const webhooks = await webhookDAL.findAllWebhooks(projectId, environment);
const toBeTriggeredHooks = webhooks.filter(
@@ -134,21 +184,20 @@ export const fnTriggerWebhook = async ({
);
if (!toBeTriggeredHooks.length) return;
logger.info({ environment, secretPath, projectId }, "Secret webhook job started");
const project = await projectDAL.findById(projectId);
let { projectName } = event.payload;
if (!projectName) {
const project = await projectDAL.findById(event.payload.projectId);
projectName = project.name;
}
const webhooksTriggered = await Promise.allSettled(
toBeTriggeredHooks.map((hook) =>
triggerWebhookRequest(
hook,
secretManagerDecryptor,
getWebhookPayload("secrets.modified", {
workspaceName: project.name,
workspaceId: projectId,
environment,
secretPath,
type: hook.type
})
)
)
toBeTriggeredHooks.map((hook) => {
const formattedEvent = {
type: event.type,
payload: { ...event.payload, type: hook.type, projectName }
} as TWebhookPayloads;
return triggerWebhookRequest(hook, secretManagerDecryptor, getWebhookPayload(formattedEvent));
})
);
// filter hooks by status
@@ -16,7 +16,8 @@ import {
TDeleteWebhookDTO,
TListWebhookDTO,
TTestWebhookDTO,
TUpdateWebhookDTO
TUpdateWebhookDTO,
WebhookEvents
} from "./webhook-types";
type TWebhookServiceFactoryDep = {
@@ -144,12 +145,15 @@ export const webhookServiceFactory = ({
await triggerWebhookRequest(
webhook,
(value) => secretManagerDecryptor({ cipherTextBlob: value }).toString(),
getWebhookPayload("test", {
workspaceName: project.name,
workspaceId: webhook.projectId,
environment: webhook.environment.slug,
secretPath: webhook.secretPath,
type: webhook.type
getWebhookPayload({
type: "test" as WebhookEvents.SecretModified,
payload: {
projectName: project.name,
projectId: webhook.projectId,
environment: webhook.environment.slug,
secretPath: webhook.secretPath,
type: webhook.type
}
})
);
} catch (err) {
@@ -30,3 +30,36 @@ export enum WebhookType {
GENERAL = "general",
SLACK = "slack"
}
export enum WebhookEvents {
SecretModified = "secrets.modified",
SecretReminderExpired = "secrets.reminder-expired",
TestEvent = "test"
}
type TWebhookSecretModifiedEventPayload = {
type: WebhookEvents.SecretModified;
payload: {
projectName?: string;
projectId: string;
environment: string;
secretPath?: string;
type?: string | null;
};
};
type TWebhookSecretReminderEventPayload = {
type: WebhookEvents.SecretReminderExpired;
payload: {
projectName?: string;
projectId: string;
environment: string;
secretPath?: string;
type?: string | null;
secretName: string;
secretId: string;
reminderNote?: string | null;
};
};
export type TWebhookPayloads = TWebhookSecretModifiedEventPayload | TWebhookSecretReminderEventPayload;