Skip to content

Commit 86e3aa8

Browse files
committed
feat: webhooks list in dashboard (#7505)
# [Dashboard] Feature: Add Webhook Management UI ![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/vKjRiOG6EUjD7XH4cppU/38dbe90b-2bd9-47b6-b190-3c257e99d2fe.png) ![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/vKjRiOG6EUjD7XH4cppU/d0a40295-c1c7-45c5-9b23-224a2a631b83.png) ![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/vKjRiOG6EUjD7XH4cppU/42d5337d-fcfe-4ef7-a349-1f596013e003.png) ![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/vKjRiOG6EUjD7XH4cppU/b7b89fe6-a5f7-4961-b927-bfc754c6665f.png) ![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/vKjRiOG6EUjD7XH4cppU/88916c15-a501-4ceb-9267-a23290f683b9.png) ## Notes for the reviewer This PR adds a new webhook management UI to the dashboard, allowing users to create, edit, delete, and monitor webhooks. The implementation includes: - A new overview page for webhooks with a table to display all configured webhooks - Create/edit webhook modals with form validation - Topic selector for webhook event subscriptions - Delete confirmation with activity warning - Webhook metrics display showing request counts and error rates - API integration for webhook configuration management ## How to test Navigate to the webhooks section in a project to view the new UI. Create a new webhook, edit its configuration, and test the delete functionality. Verify that metrics are displayed correctly for existing webhooks. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced a comprehensive webhooks management interface, including creation, editing, deletion, and topic selection with optional JSON filters. * Added analytics dashboards for webhooks, featuring charts for request status codes and latency percentiles, with filtering by webhook and date range. * Implemented feature flag-based tab navigation for "Overview" and "Analytics" in the webhooks section. * Provided detailed webhook metrics, including recent activity and error rates. * **Bug Fixes** * Improved error handling and user feedback for webhook configuration and analytics operations. * **Chores** * Added PostHog server-side analytics integration. * Updated environment variable naming for analytics configuration. * Added new dependency for analytics support. <!-- end of auto-generated comment: release notes by coderabbit.ai --> <!-- start pr-codex --> --- ## PR-Codex overview This PR introduces webhook analytics features and enhances the webhook management system, including new types for webhook statistics, UI components for creating and editing webhooks, and improvements in data fetching and handling. ### Detailed summary - Added `posthog-node` dependency for server-side analytics. - Updated environment variable from `NEXT_PUBLIC_POSTHOG_API_KEY` to `NEXT_PUBLIC_POSTHOG_KEY`. - Introduced interfaces for `WebhookRequestStats`, `WebhookLatencyStats`, and `WebhookSummaryStats`. - Created `CreateWebhookConfigModal` and `EditWebhookConfigModal` components for managing webhooks. - Implemented `WebhooksAnalytics` and `WebhookAnalyticsServer` components for displaying analytics data. - Enhanced `WebhookMetrics` to show metrics and error rates. - Added `isFeatureFlagEnabled` function for feature flag checks. - Implemented data fetching functions for webhook metrics in `analytics.ts`. - Updated the webhook configuration management with new API endpoints and improved error handling. - Created `TopicSelectorModal` for selecting topics with optional filters. - Improved UI components for better user experience in managing webhooks. > The following files were skipped due to too many changes: `pnpm-lock.yaml` > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent aa1035d commit 86e3aa8

23 files changed

+2306
-78
lines changed

apps/dashboard/.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ NEXT_PUBLIC_TYPESENSE_CONTRACT_API_KEY=
3737

3838
# posthog API key
3939
# - not required for prod/staging
40-
NEXT_PUBLIC_POSTHOG_API_KEY="ignored"
40+
NEXT_PUBLIC_POSTHOG_KEY=""
4141

4242
# Stripe Customer portal
4343
NEXT_PUBLIC_STRIPE_KEY=

apps/dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"papaparse": "^5.5.3",
5656
"pluralize": "^8.0.0",
5757
"posthog-js": "1.256.1",
58+
"posthog-node": "^5.4.0",
5859
"prettier": "3.6.2",
5960
"qrcode": "^1.5.3",
6061
"react": "19.1.0",
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import "server-only";
2+
import { PostHog } from "posthog-node";
3+
4+
let posthogServer: PostHog | null = null;
5+
6+
function getPostHogServer(): PostHog | null {
7+
if (!posthogServer && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
8+
posthogServer = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
9+
host: "https://us.i.posthog.com",
10+
});
11+
}
12+
return posthogServer;
13+
}
14+
15+
/**
16+
* Check if a feature flag is enabled for a specific user
17+
* @param flagKey - The feature flag key
18+
* @param userEmail - The user's email address for filtering
19+
*/
20+
export async function isFeatureFlagEnabled(
21+
flagKey: string,
22+
userEmail?: string,
23+
): Promise<boolean> {
24+
// For localdev environments where Posthog is not running, enable all feature flags.
25+
if (!posthogServer) {
26+
return true;
27+
}
28+
29+
try {
30+
const client = getPostHogServer();
31+
if (client && userEmail) {
32+
const isEnabled = await client.isFeatureEnabled(flagKey, userEmail, {
33+
personProperties: {
34+
email: userEmail,
35+
},
36+
});
37+
if (isEnabled !== undefined) {
38+
return isEnabled;
39+
}
40+
}
41+
} catch (error) {
42+
console.error(`Error checking feature flag ${flagKey}:`, error);
43+
}
44+
return false;
45+
}

apps/dashboard/src/@/api/analytics.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import type {
1212
UserOpStats,
1313
WalletStats,
1414
WalletUserStats,
15+
WebhookLatencyStats,
16+
WebhookRequestStats,
17+
WebhookSummaryStats,
1518
} from "@/types/analytics";
1619
import { getAuthToken } from "./auth-token";
1720
import { getChains } from "./chain";
@@ -482,6 +485,60 @@ export async function getEngineCloudMethodUsage(
482485
return json.data as EngineCloudStats[];
483486
}
484487

488+
export async function getWebhookSummary(
489+
params: AnalyticsQueryParams & { webhookId: string },
490+
): Promise<{ data: WebhookSummaryStats[] } | { error: string }> {
491+
const searchParams = buildSearchParams(params);
492+
searchParams.append("webhookId", params.webhookId);
493+
494+
const res = await fetchAnalytics(
495+
`v2/webhook/summary?${searchParams.toString()}`,
496+
);
497+
if (!res.ok) {
498+
const reason = await res.text();
499+
return { error: reason };
500+
}
501+
502+
return (await res.json()) as { data: WebhookSummaryStats[] };
503+
}
504+
505+
export async function getWebhookRequests(
506+
params: AnalyticsQueryParams & { webhookId?: string },
507+
): Promise<{ data: WebhookRequestStats[] } | { error: string }> {
508+
const searchParams = buildSearchParams(params);
509+
if (params.webhookId) {
510+
searchParams.append("webhookId", params.webhookId);
511+
}
512+
513+
const res = await fetchAnalytics(
514+
`v2/webhook/requests?${searchParams.toString()}`,
515+
);
516+
if (!res.ok) {
517+
const reason = await res.text();
518+
return { error: reason };
519+
}
520+
521+
return (await res.json()) as { data: WebhookRequestStats[] };
522+
}
523+
524+
export async function getWebhookLatency(
525+
params: AnalyticsQueryParams & { webhookId?: string },
526+
): Promise<{ data: WebhookLatencyStats[] } | { error: string }> {
527+
const searchParams = buildSearchParams(params);
528+
if (params.webhookId) {
529+
searchParams.append("webhookId", params.webhookId);
530+
}
531+
const res = await fetchAnalytics(
532+
`v2/webhook/latency?${searchParams.toString()}`,
533+
);
534+
if (!res.ok) {
535+
const reason = await res.text();
536+
return { error: reason };
537+
}
538+
539+
return (await res.json()) as { data: WebhookLatencyStats[] };
540+
}
541+
485542
export async function getInsightChainUsage(
486543
params: AnalyticsQueryParams,
487544
): Promise<{ data: InsightChainStats[] } | { errorMessage: string }> {

0 commit comments

Comments
 (0)