Skip to content

Alert Webhook improvements #1703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/slow-olives-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@trigger.dev/sdk": patch
---

You can add Alerts in the dashboard. One of these is a webhook, which this change greatly improves.

The main change is that there's now an SDK function to verify and parse them (similar to Stripe SDK).

```ts
const event = await webhooks.constructEvent(request, process.env.ALERT_WEBHOOK_SECRET!);
```

If the signature you provide matches the one from the dashboard when you create the webhook, you will get a nicely typed object back for these three types:
- "alert.run.failed"
- "alert.deployment.success"
- "alert.deployment.failed"
1 change: 1 addition & 0 deletions apps/webapp/app/models/projectAlert.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { EncryptedSecretValueSchema } from "~/services/secrets/secretStore.serve
export const ProjectAlertWebhookProperties = z.object({
secret: EncryptedSecretValueSchema,
url: z.string(),
version: z.string().optional().default("v1"),
});

export type ProjectAlertWebhookProperties = z.infer<typeof ProjectAlertWebhookProperties>;
Expand Down
50 changes: 50 additions & 0 deletions apps/webapp/app/routes/internal.webhooks.tester.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ActionFunctionArgs, json } from "@remix-run/server-runtime";
import { webhooks } from "@trigger.dev/sdk/v3";
import { WebhookError } from "@trigger.dev/sdk/v3";
import { logger } from "~/services/logger.server";

/*
This route is for testing our webhooks
*/
export async function action({ request }: ActionFunctionArgs) {
// Make sure this is a POST request
if (request.method !== "POST") {
return json({ error: "[Webhook Internal Test] Method not allowed" }, { status: 405 });
}

const clonedRequest = request.clone();
const rawBody = await clonedRequest.text();
logger.log("[Webhook Internal Test] Raw body:", { rawBody });

try {
// Construct and verify the webhook event
const event = await webhooks.constructEvent(request, process.env.INTERNAL_TEST_WEBHOOK_SECRET!);

// Handle the webhook event
logger.log("[Webhook Internal Test] Received verified webhook:", event);

// Process the event based on its type
switch (event.type) {
default:
logger.log(`[Webhook Internal Test] Unhandled event type: ${event.type}`);
}

// Return a success response
return json({ received: true }, { status: 200 });
} catch (err) {
// Handle webhook errors
if (err instanceof WebhookError) {
logger.error("[Webhook Internal Test] Webhook error:", { message: err.message });
return json({ error: err.message }, { status: 400 });
}

if (err instanceof Error) {
logger.error("[Webhook Internal Test] Error processing webhook:", { message: err.message });
return json({ error: err.message }, { status: 400 });
}

// Handle other errors
logger.error("[Webhook Internal Test] Error processing webhook:", { err });
return json({ error: "Internal server error" }, { status: 500 });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export class CreateAlertChannelService extends BaseService {
return {
url: channel.url,
secret: await encryptSecret(env.ENCRYPTION_KEY, channel.secret ?? nanoid()),
version: "v2",
};
case "SLACK":
return {
Expand Down
Loading