From 8a65efa441dee92c945c3fce891adeeb72bebbec Mon Sep 17 00:00:00 2001 From: Amit Sharma Date: Wed, 23 Jul 2025 15:08:21 -0700 Subject: [PATCH] feat: add PostHog analytics integration - Add PostHog client configuration in ui/lib/posthog.ts - Integrate PostHog tracking in auth forms and provider workflows - Add PostHog dependencies (posthog-js and posthog-node) - Update Next.js config for PostHog integration - Add environment variables for PostHog configuration - Update changelog with PostHog integration entry --- .env | 4 ++++ ui/CHANGELOG.md | 2 ++ ui/app/providers.tsx | 19 +++++++++++++++- ui/components/auth/oss/auth-form.tsx | 22 +++++++++++++++++++ .../workflow/forms/test-connection-form.tsx | 8 +++++++ ui/lib/posthog.ts | 11 ++++++++++ ui/next.config.js | 8 +++---- ui/package-lock.json | 2 ++ ui/package.json | 2 ++ 9 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 ui/lib/posthog.ts diff --git a/.env b/.env index 4a0e1ef2d4..4bf45159bb 100644 --- a/.env +++ b/.env @@ -150,3 +150,7 @@ LANGSMITH_TRACING=false LANGSMITH_ENDPOINT="https://api.smith.langchain.com" LANGSMITH_API_KEY="" LANGCHAIN_PROJECT="" + +# posthog integration +NEXT_PUBLIC_POSTHOG_KEY="" +NEXT_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index 0915f7aea4..9fa8853737 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to the **Prowler UI** are documented in this file. ### 🚀 Added +- PostHog analytics integration +[(#PR_NUMBER)](https://github.com/prowler-cloud/prowler/pull/PR_NUMBER) - Mutelist configuration form [(#8190)](https://github.com/prowler-cloud/prowler/pull/8190) - SAML login integration [(#8203)](https://github.com/prowler-cloud/prowler/pull/8203) - Resource view [(#7760)](https://github.com/prowler-cloud/prowler/pull/7760) diff --git a/ui/app/providers.tsx b/ui/app/providers.tsx index 942bce43dc..d5ff088ad6 100644 --- a/ui/app/providers.tsx +++ b/ui/app/providers.tsx @@ -5,6 +5,9 @@ import { useRouter } from "next/navigation"; import { SessionProvider } from "next-auth/react"; import { ThemeProvider as NextThemesProvider } from "next-themes"; import { ThemeProviderProps } from "next-themes/dist/types"; +import posthog from "posthog-js"; +import { PostHogProvider as PHProvider } from "posthog-js/react"; +import { useEffect } from "react"; import * as React from "react"; export interface ProvidersProps { @@ -15,10 +18,24 @@ export interface ProvidersProps { export function Providers({ children, themeProps }: ProvidersProps) { const router = useRouter(); + useEffect(() => { + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { + api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST, + ui_host: process.env.NEXT_PUBLIC_POSTHOG_HOST, + autocapture: false, + defaults: "2025-05-24", + capture_exceptions: true, + capture_pageview: false, + capture_pageleave: false, + }); + }, []); + return ( - {children} + + {children} + ); diff --git a/ui/components/auth/oss/auth-form.tsx b/ui/components/auth/oss/auth-form.tsx index a965204dbc..7c68363af8 100644 --- a/ui/components/auth/oss/auth-form.tsx +++ b/ui/components/auth/oss/auth-form.tsx @@ -4,6 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { Icon } from "@iconify/react"; import { Button, Checkbox, Divider, Link, Tooltip } from "@nextui-org/react"; import { useRouter, useSearchParams } from "next/navigation"; +import posthog from "posthog-js"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; @@ -80,6 +81,8 @@ export const AuthForm = ({ const isSamlMode = form.watch("isSamlMode"); const onSubmit = async (data: z.infer) => { + //getting an new posthog init + posthog.reset(true); if (type === "sign-in") { if (data.isSamlMode) { const email = data.email.toLowerCase(); @@ -107,6 +110,11 @@ export const AuthForm = ({ password: data.password, }); if (result?.message === "Success") { + posthog.identify(data.email.toLowerCase()); + posthog.capture("user_login", { + email: data.email.toLocaleLowerCase(), + timestamp: Date.now(), + }); router.push("/"); } else if (result?.errors && "credentials" in result.errors) { form.setError("email", { @@ -128,6 +136,20 @@ export const AuthForm = ({ const newUser = await createNewUser(data); if (!newUser.errors) { + let firstName = ""; + let lastName = ""; + if (data.name) { + const nameParts = data.name.trim().split(" "); + firstName = nameParts[0] || ""; + lastName = nameParts.slice(1).join(" ") || ""; + } + posthog.capture("user_registered", { + email: data.email.toLocaleLowerCase(), + firstname: firstName, + lastname: lastName, + company: data.company || "", + timestamp: Date.now(), + }); toast({ title: "Success!", description: "The user was registered successfully.", diff --git a/ui/components/providers/workflow/forms/test-connection-form.tsx b/ui/components/providers/workflow/forms/test-connection-form.tsx index 6fdc6e2215..9cd0bb7718 100644 --- a/ui/components/providers/workflow/forms/test-connection-form.tsx +++ b/ui/components/providers/workflow/forms/test-connection-form.tsx @@ -5,6 +5,7 @@ import { Icon } from "@iconify/react"; import { Checkbox } from "@nextui-org/react"; import Link from "next/link"; import { useRouter } from "next/navigation"; +import posthog from "posthog-js"; import { useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; @@ -145,6 +146,13 @@ export const TestConnectionForm = ({ }); } else { setIsRedirecting(true); + // Capture PostHog event for successful cloud connection + posthog.capture("cloud_connection_success", { + provider_type: providerType, + provider_alias: providerData.data.attributes.alias, + scan_type: form.watch("runOnce") ? "single" : "scheduled", + timestamp: Date.now(), + }); router.push("/scans"); } } catch (error) { diff --git a/ui/lib/posthog.ts b/ui/lib/posthog.ts new file mode 100644 index 0000000000..057cdcb5a3 --- /dev/null +++ b/ui/lib/posthog.ts @@ -0,0 +1,11 @@ +import { PostHog } from "posthog-node"; + +// NOTE: This is a Node.js client, so you can use it for sending events from the server side to PostHog. +export default function PostHogClient() { + const posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { + host: process.env.NEXT_PUBLIC_POSTHOG_HOST, + flushAt: 1, + flushInterval: 0, + }); + return posthogClient; +} diff --git a/ui/next.config.js b/ui/next.config.js index af64900d4c..41e031f484 100644 --- a/ui/next.config.js +++ b/ui/next.config.js @@ -4,12 +4,12 @@ // 'unsafe-eval' is configured under `script-src` because it is required by NextJS for development mode const cspHeader = ` default-src 'self'; - script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com https://www.googletagmanager.com; - connect-src 'self' https://api.iconify.design https://api.simplesvg.com https://api.unisvg.com https://js.stripe.com https://www.googletagmanager.com; - img-src 'self' https://www.google-analytics.com https://www.googletagmanager.com; + script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com https://www.googletagmanager.com https://*.posthog.com; + connect-src 'self' https://api.iconify.design https://api.simplesvg.com https://api.unisvg.com https://js.stripe.com https://www.googletagmanager.com https://*.posthog.com; + img-src 'self' https://www.google-analytics.com https://www.googletagmanager.com https://*.posthog.com; font-src 'self'; style-src 'self' 'unsafe-inline'; - frame-src 'self' https://js.stripe.com https://www.googletagmanager.com; + frame-src 'self' https://js.stripe.com https://www.googletagmanager.com https://*.posthog.com; frame-ancestors 'none'; `; diff --git a/ui/package-lock.json b/ui/package-lock.json index 326bf8717a..5c22690d0d 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -47,6 +47,8 @@ "next": "^14.2.30", "next-auth": "^5.0.0-beta.25", "next-themes": "^0.2.1", + "posthog-js": "^1.256.1", + "posthog-node": "^5.1.1", "radix-ui": "^1.1.3", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/ui/package.json b/ui/package.json index da794f3b33..d4309c971a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -39,6 +39,8 @@ "next": "^14.2.30", "next-auth": "^5.0.0-beta.25", "next-themes": "^0.2.1", + "posthog-js": "^1.256.1", + "posthog-node": "^5.1.1", "radix-ui": "^1.1.3", "react": "^18.3.1", "react-dom": "^18.3.1",