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
This commit is contained in:
Amit Sharma
2025-07-23 15:08:21 -07:00
parent aa843b823c
commit 8a65efa441
9 changed files with 73 additions and 5 deletions
+4
View File
@@ -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
+2
View File
@@ -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)
+18 -1
View File
@@ -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 (
<SessionProvider>
<NextUIProvider navigate={router.push}>
<NextThemesProvider {...themeProps}>{children}</NextThemesProvider>
<NextThemesProvider {...themeProps}>
<PHProvider client={posthog}>{children}</PHProvider>
</NextThemesProvider>
</NextUIProvider>
</SessionProvider>
);
+22
View File
@@ -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<typeof formSchema>) => {
//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.",
@@ -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) {
+11
View File
@@ -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;
}
+4 -4
View File
@@ -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';
`;
+2
View File
@@ -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",
+2
View File
@@ -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",