Skip to content
Closed
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
2 changes: 1 addition & 1 deletion apps/dashboard/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ NEXT_PUBLIC_TYPESENSE_CONTRACT_API_KEY=

# posthog API key
# - not required for prod/staging
NEXT_PUBLIC_POSTHOG_API_KEY="ignored"
NEXT_PUBLIC_POSTHOG_KEY=""

# Stripe Customer portal
NEXT_PUBLIC_STRIPE_KEY=
Expand Down
8 changes: 4 additions & 4 deletions apps/dashboard/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ function getAbsolutePath(value: string): string {
return dirname(require.resolve(join(value, "package.json")));
}
const config: StorybookConfig = {
stories: ["../src/**/*.stories.tsx"],
addons: [
getAbsolutePath("@storybook/addon-onboarding"),
getAbsolutePath("@storybook/addon-links"),
getAbsolutePath("@chromatic-com/storybook"),
getAbsolutePath("@storybook/addon-docs"),
],
features: {
experimentalRSC: true,
},
framework: {
name: getAbsolutePath("@storybook/nextjs"),
options: {},
Expand All @@ -26,8 +28,6 @@ const config: StorybookConfig = {
},
},
staticDirs: ["../public"],
features: {
experimentalRSC: true,
},
stories: ["../src/**/*.stories.tsx"],
};
export default config;
54 changes: 25 additions & 29 deletions apps/dashboard/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import type { Preview } from "@storybook/nextjs";
import "../src/global.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { MoonIcon, SunIcon } from "lucide-react";
import { ThemeProvider, useTheme } from "next-themes";
import { Inter as interFont } from "next/font/google";
import { ThemeProvider, useTheme } from "next-themes";
// biome-ignore lint/style/useImportType: <explanation>
import React from "react";
import { useEffect } from "react";
import React, { useEffect } from "react";
import { Toaster } from "sonner";
import { Button } from "../src/@/components/ui/button";

Expand All @@ -18,45 +17,33 @@ const fontSans = interFont({
});

const customViewports = {
xs: {
// Regular sized phones (iphone 15 / 15 pro)
name: "iPhone",
styles: {
width: "390px",
height: "844px",
},
},
sm: {
// Larger phones (iphone 15 plus / 15 pro max)
name: "iPhone Plus",
styles: {
width: "430px",
height: "932px",
width: "430px",
},
},
};

const preview: Preview = {
parameters: {
viewport: {
viewports: customViewports,
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
xs: {
// Regular sized phones (iphone 15 / 15 pro)
name: "iPhone",
styles: {
height: "844px",
width: "390px",
},
},
};

const preview: Preview = {
decorators: [
(Story) => {
return (
<ThemeProvider
attribute="class"
defaultTheme="dark"
disableTransitionOnChange
enableSystem={false}
defaultTheme="dark"
>
<StoryLayout>
<Story />
Expand All @@ -65,13 +52,22 @@ const preview: Preview = {
);
},
],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
viewport: {
viewports: customViewports,
},
},
};

export default preview;

function StoryLayout(props: {
children: React.ReactNode;
}) {
function StoryLayout(props: { children: React.ReactNode }) {
const { setTheme, theme } = useTheme();

useEffect(() => {
Expand All @@ -83,10 +79,10 @@ function StoryLayout(props: {
<div className="flex min-h-dvh min-w-0 flex-col bg-background text-foreground">
<div className="fixed right-0 bottom-0 z-50 flex justify-end gap-2 p-4">
<Button
className="h-auto w-auto shrink-0 rounded-full p-2"
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
size="sm"
variant="outline"
className="h-auto w-auto shrink-0 rounded-full p-2"
>
{theme === "dark" ? (
<MoonIcon className="size-4" />
Expand Down
1 change: 1 addition & 0 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"papaparse": "^5.5.3",
"pluralize": "^8.0.0",
"posthog-js": "1.256.1",
"posthog-node": "^5.3.1",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the posthog-node package version for security and compatibility.

The addition of posthog-node for server-side PostHog integration is appropriate. Please verify that version ^5.3.1 is the latest stable version and free from security vulnerabilities.


🌐 Web query:

What is the latest stable version of posthog-node npm package and are there any security advisories for version 5.3.1?

💡 Result:

The latest stable version of the posthog-node npm package is 4.18.0, released on May 27, 2025. (classic.yarnpkg.com)

Regarding version 5.3.1, there is no record of such a release in the official PostHog repositories or npm registry. The most recent version in the 5.x.x series is 5.0.0-alpha.1, which is an alpha release and not considered stable. (classic.yarnpkg.com)

Additionally, the PostHog Node.js repository does not have any published security advisories. (github.com)

If you are using version 5.3.1, it may be a custom or unofficial release. In such cases, it's advisable to consult the source from which you obtained this version for any security advisories or updates.

Citations:


Correct posthog-node version in package.json

It looks like ^5.3.1 isn’t published on npm—the latest stable is 4.18.0. Please update the dependency or confirm the correct source:

  • File: apps/dashboard/package.json, line 58

Suggested change:

-   "posthog-node": "^5.3.1",
+   "posthog-node": "^4.18.0",

If you intended to use a custom or alpha release, document its origin and verify any security advisories with the provider.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"posthog-node": "^5.3.1",
"posthog-node": "^4.18.0",
🤖 Prompt for AI Agents
In apps/dashboard/package.json at line 58, the posthog-node dependency version
"^5.3.1" is not published on npm. Update the version to the latest stable
release "4.18.0" or the correct published version. If you are using a custom or
alpha release, add documentation about its source and verify any security
advisories with the provider.

"prettier": "3.6.2",
"qrcode": "^1.5.3",
"react": "19.1.0",
Expand Down
16 changes: 8 additions & 8 deletions apps/dashboard/public/assets/examples/example.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
[
{
"name": "Your Collection #1",
"description": "Remember to replace this description",
"image": "ipfs://NewUriToReplace/1.png",
"attributes": [
{
"trait_type": "Background",
Expand Down Expand Up @@ -32,12 +29,12 @@
"trait_type": "Top lid",
"value": "High"
}
]
],
"description": "Remember to replace this description",
"image": "ipfs://NewUriToReplace/1.png",
"name": "Your Collection #1"
},
{
"name": "Your Collection #2",
"description": "Remember to replace this description",
"image": "ipfs://NewUriToReplace/2.png",
"attributes": [
{
"trait_type": "Background",
Expand Down Expand Up @@ -67,6 +64,9 @@
"trait_type": "Top lid",
"value": "Middle"
}
]
],
"description": "Remember to replace this description",
"image": "ipfs://NewUriToReplace/2.png",
"name": "Your Collection #2"
}
]
40 changes: 40 additions & 0 deletions apps/dashboard/src/@/analytics/posthog-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import "server-only";
import { PostHog } from "posthog-node";

let posthogServer: PostHog | null = null;

function getPostHogServer(): PostHog | null {
if (!posthogServer && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
posthogServer = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
host: "https://us.i.posthog.com",
});
}
return posthogServer;
}

/**
* Check if a feature flag is enabled for a specific user
* @param flagKey - The feature flag key
* @param userEmail - The user's email address for filtering
*/
export async function isFeatureFlagEnabled(
flagKey: string,
userEmail?: string,
): Promise<boolean> {
try {
const client = getPostHogServer();
if (client && userEmail) {
const isEnabled = await client.isFeatureEnabled(flagKey, userEmail, {
personProperties: {
email: userEmail,
},
});
if (isEnabled !== undefined) {
return isEnabled;
}
}
} catch (error) {
console.error(`Error checking feature flag ${flagKey}:`, error);
}
return false;
}
57 changes: 57 additions & 0 deletions apps/dashboard/src/@/api/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import type {
UserOpStats,
WalletStats,
WalletUserStats,
WebhookLatencyStats,
WebhookRequestStats,
WebhookSummaryStats,
} from "@/types/analytics";
import { getAuthToken } from "./auth-token";
import { getChains } from "./chain";
Expand Down Expand Up @@ -482,6 +485,60 @@ export async function getEngineCloudMethodUsage(
return json.data as EngineCloudStats[];
}

export async function getWebhookSummary(
params: AnalyticsQueryParams & { webhookId: string },
): Promise<{ data: WebhookSummaryStats[] } | { error: string }> {
const searchParams = buildSearchParams(params);
searchParams.append("webhookId", params.webhookId);

const res = await fetchAnalytics(
`v2/webhook/summary?${searchParams.toString()}`,
);
if (!res.ok) {
const reason = await res.text();
return { error: reason };
}

return (await res.json()) as { data: WebhookSummaryStats[] };
}

export async function getWebhookRequests(
params: AnalyticsQueryParams & { webhookId?: string },
): Promise<{ data: WebhookRequestStats[] } | { error: string }> {
const searchParams = buildSearchParams(params);
if (params.webhookId) {
searchParams.append("webhookId", params.webhookId);
}

const res = await fetchAnalytics(
`v2/webhook/requests?${searchParams.toString()}`,
);
if (!res.ok) {
const reason = await res.text();
return { error: reason };
}

return (await res.json()) as { data: WebhookRequestStats[] };
}

export async function getWebhookLatency(
params: AnalyticsQueryParams & { webhookId?: string },
): Promise<{ data: WebhookLatencyStats[] } | { error: string }> {
const searchParams = buildSearchParams(params);
if (params.webhookId) {
searchParams.append("webhookId", params.webhookId);
}
const res = await fetchAnalytics(
`v2/webhook/latency?${searchParams.toString()}`,
);
if (!res.ok) {
const reason = await res.text();
return { error: reason };
}

return (await res.json()) as { data: WebhookLatencyStats[] };
}

export async function getInsightChainUsage(
params: AnalyticsQueryParams,
): Promise<{ data: InsightChainStats[] } | { errorMessage: string }> {
Expand Down
Loading
Loading