Skip to content

Commit 440d413

Browse files
authored
Alert Webhook improvements (#1703)
* WIP with webhook SDK function and types * JSDocs added to the schema * Webhooks are working * Expanded the alert docs * Remove duplicate export of waitUntil.js * Use uncrypto * Don’t rate limit webhooks * Create slow-olives-fix.md
1 parent a2c70b4 commit 440d413

17 files changed

+1023
-328
lines changed

.changeset/slow-olives-fix.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"@trigger.dev/sdk": patch
3+
---
4+
5+
You can add Alerts in the dashboard. One of these is a webhook, which this change greatly improves.
6+
7+
The main change is that there's now an SDK function to verify and parse them (similar to Stripe SDK).
8+
9+
```ts
10+
const event = await webhooks.constructEvent(request, process.env.ALERT_WEBHOOK_SECRET!);
11+
```
12+
13+
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:
14+
- "alert.run.failed"
15+
- "alert.deployment.success"
16+
- "alert.deployment.failed"

apps/webapp/app/models/projectAlert.server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { EncryptedSecretValueSchema } from "~/services/secrets/secretStore.serve
44
export const ProjectAlertWebhookProperties = z.object({
55
secret: EncryptedSecretValueSchema,
66
url: z.string(),
7+
version: z.string().optional().default("v1"),
78
});
89

910
export type ProjectAlertWebhookProperties = z.infer<typeof ProjectAlertWebhookProperties>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ActionFunctionArgs, json } from "@remix-run/server-runtime";
2+
import { webhooks } from "@trigger.dev/sdk/v3";
3+
import { WebhookError } from "@trigger.dev/sdk/v3";
4+
import { logger } from "~/services/logger.server";
5+
6+
/*
7+
This route is for testing our webhooks
8+
*/
9+
export async function action({ request }: ActionFunctionArgs) {
10+
// Make sure this is a POST request
11+
if (request.method !== "POST") {
12+
return json({ error: "[Webhook Internal Test] Method not allowed" }, { status: 405 });
13+
}
14+
15+
const clonedRequest = request.clone();
16+
const rawBody = await clonedRequest.text();
17+
logger.log("[Webhook Internal Test] Raw body:", { rawBody });
18+
19+
try {
20+
// Construct and verify the webhook event
21+
const event = await webhooks.constructEvent(request, process.env.INTERNAL_TEST_WEBHOOK_SECRET!);
22+
23+
// Handle the webhook event
24+
logger.log("[Webhook Internal Test] Received verified webhook:", event);
25+
26+
// Process the event based on its type
27+
switch (event.type) {
28+
default:
29+
logger.log(`[Webhook Internal Test] Unhandled event type: ${event.type}`);
30+
}
31+
32+
// Return a success response
33+
return json({ received: true }, { status: 200 });
34+
} catch (err) {
35+
// Handle webhook errors
36+
if (err instanceof WebhookError) {
37+
logger.error("[Webhook Internal Test] Webhook error:", { message: err.message });
38+
return json({ error: err.message }, { status: 400 });
39+
}
40+
41+
if (err instanceof Error) {
42+
logger.error("[Webhook Internal Test] Error processing webhook:", { message: err.message });
43+
return json({ error: err.message }, { status: 400 });
44+
}
45+
46+
// Handle other errors
47+
logger.error("[Webhook Internal Test] Error processing webhook:", { err });
48+
return json({ error: "Internal server error" }, { status: 500 });
49+
}
50+
}

apps/webapp/app/v3/services/alerts/createAlertChannel.server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export class CreateAlertChannelService extends BaseService {
100100
return {
101101
url: channel.url,
102102
secret: await encryptSecret(env.ENCRYPTION_KEY, channel.secret ?? nanoid()),
103+
version: "v2",
103104
};
104105
case "SLACK":
105106
return {

0 commit comments

Comments
 (0)