From a5d6e769db45f6373b252ef7feba6c74b05f5eac Mon Sep 17 00:00:00 2001 From: AmineAfia Date: Wed, 28 May 2025 21:20:24 +0000 Subject: [PATCH] Add webhook API functions for insight dashboard (#7171) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ## PR-Codex overview This PR introduces functionality for managing webhooks in the application. It includes a temporary `WebhooksPage` for displaying webhook data and implements several API functions for creating, retrieving, deleting, and testing webhooks. ### Detailed summary - Added `WebhooksPage` component in `page.tsx` to display webhook data. - Implemented `getWebhooks` function to fetch webhooks from the API. - Created interfaces for webhook responses and payloads. - Added `createWebhook`, `deleteWebhook`, and `testWebhook` functions for managing webhooks. - Handled API errors with appropriate responses. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` ## Summary by CodeRabbit - **New Features** - Introduced the ability to create, view, delete, and test webhooks through the dashboard interface, allowing users to manage webhook integrations more easily. - Added a new dashboard page displaying webhook data for better visibility and management. --- apps/dashboard/src/@/api/insight/webhooks.ts | 202 ++++++++++++++++++ .../(sidebar)/webhooks/page.tsx | 8 + 2 files changed, 210 insertions(+) create mode 100644 apps/dashboard/src/@/api/insight/webhooks.ts create mode 100644 apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx diff --git a/apps/dashboard/src/@/api/insight/webhooks.ts b/apps/dashboard/src/@/api/insight/webhooks.ts new file mode 100644 index 00000000000..51aeeda4890 --- /dev/null +++ b/apps/dashboard/src/@/api/insight/webhooks.ts @@ -0,0 +1,202 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +"use server"; + +import { getAuthToken } from "app/(app)/api/lib/getAuthToken"; +import { THIRDWEB_INSIGHT_API_DOMAIN } from "constants/urls"; + +interface WebhookResponse { + id: string; + name: string; + team_id: string; + project_id: string; + webhook_url: string; + webhook_secret: string; + filters: WebhookFilters; + suspended_at: string | null; + suspended_reason: string | null; + disabled: boolean; + created_at: string; + updated_at: string | null; +} + +interface WebhookFilters { + "v1.events"?: { + chain_ids?: string[]; + addresses?: string[]; + signatures?: Array<{ + sig_hash: string; + abi?: string; + params?: Record; + }>; + }; + "v1.transactions"?: { + chain_ids?: string[]; + from_addresses?: string[]; + to_addresses?: string[]; + signatures?: Array<{ + sig_hash: string; + abi?: string; + params?: Record; + }>; + }; +} + +interface CreateWebhookPayload { + name: string; + webhook_url: string; + filters: WebhookFilters; +} + +interface WebhooksListResponse { + data: WebhookResponse[]; + error?: string; +} + +interface WebhookSingleResponse { + data: WebhookResponse | null; + error?: string; +} + +interface TestWebhookPayload { + webhook_url: string; + type?: "event" | "transaction"; +} + +interface TestWebhookResponse { + success: boolean; + error?: string; +} + +// biome-ignore lint/correctness/noUnusedVariables: will be used in the next PR +async function createWebhook( + payload: CreateWebhookPayload, + clientId: string, +): Promise { + try { + const authToken = await getAuthToken(); + const response = await fetch(`${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-client-id": clientId, + Authorization: `Bearer ${authToken}`, + }, + body: JSON.stringify(payload), + }); + + if (!response.ok) { + const errorText = await response.text(); + return { + data: null, + error: `Failed to create webhook: ${errorText}`, + }; + } + + return (await response.json()) as WebhookSingleResponse; + } catch (error) { + return { + data: null, + error: `Network or parsing error: ${error instanceof Error ? error.message : "Unknown error"}`, + }; + } +} + +export async function getWebhooks( + clientId: string, +): Promise { + try { + const authToken = await getAuthToken(); + const response = await fetch(`${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks`, { + method: "GET", + headers: { + "x-client-id": clientId, + Authorization: `Bearer ${authToken}`, + }, + }); + + if (!response.ok) { + const errorText = await response.text(); + return { + data: [], + error: `Failed to get webhooks: ${errorText}`, + }; + } + + return (await response.json()) as WebhooksListResponse; + } catch (error) { + return { + data: [], + error: `Network or parsing error: ${error instanceof Error ? error.message : "Unknown error"}`, + }; + } +} +// biome-ignore lint/correctness/noUnusedVariables: will be used in the next PR +async function deleteWebhook( + webhookId: string, + clientId: string, +): Promise { + try { + const authToken = await getAuthToken(); + const response = await fetch( + `${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks/${encodeURIComponent(webhookId)}`, + { + method: "DELETE", + headers: { + "x-client-id": clientId, + Authorization: `Bearer ${authToken}`, + }, + }, + ); + + if (!response.ok) { + const errorText = await response.text(); + return { + data: null, + error: `Failed to delete webhook: ${errorText}`, + }; + } + + return (await response.json()) as WebhookSingleResponse; + } catch (error) { + return { + data: null, + error: `Network or parsing error: ${error instanceof Error ? error.message : "Unknown error"}`, + }; + } +} +// biome-ignore lint/correctness/noUnusedVariables: will be used in the next PR +async function testWebhook( + payload: TestWebhookPayload, + clientId: string, +): Promise { + try { + const authToken = await getAuthToken(); + const response = await fetch( + `${THIRDWEB_INSIGHT_API_DOMAIN}/v1/webhooks/test`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-client-id": clientId, + Authorization: `Bearer ${authToken}`, + }, + body: JSON.stringify(payload), + }, + ); + + if (!response.ok) { + const errorText = await response.text(); + return { + success: false, + error: `Failed to test webhook: ${errorText}`, + }; + } + + return (await response.json()) as TestWebhookResponse; + } catch (error) { + return { + success: false, + error: `Network or parsing error: ${error instanceof Error ? error.message : "Unknown error"}`, + }; + } +} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx new file mode 100644 index 00000000000..7149a59a94f --- /dev/null +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx @@ -0,0 +1,8 @@ +// This is a temporary page to supress linting errors and will be implemented up in the stack +import { getWebhooks } from "@/api/insight/webhooks"; + +export default async function WebhooksPage() { + const { data: webhooks, error } = await getWebhooks("123"); + + return
{JSON.stringify({ webhooks, error })}
; +}