PostHog event pass (#246)

This commit is contained in:
Brendan Kellam 2025-03-26 16:01:29 -07:00 committed by GitHub
parent 78b8916797
commit 8e3235a56d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 81 additions and 38 deletions

View file

@ -34,7 +34,7 @@ AUTH_URL="http://localhost:3000"
# Sentry
# SENTRY_BACKEND_DSN=""
# NEXT_PUBLIC_SENTRY_WEBAPP_DSN=""
SENTRY_ENVIRONMENT="dev"
# SENTRY_ENVIRONMENT="dev"
# NEXT_PUBLIC_SENTRY_ENVIRONMENT="dev"
# SENTRY_AUTH_TOKEN=
@ -76,4 +76,6 @@ SOURCEBOT_TELEMETRY_DISABLED=true # Disables telemetry collection
# CONFIG_MAX_REPOS_NO_TOKEN=
# NODE_ENV=
# SOURCEBOT_TENANCY_MODE=single
# SOURCEBOT_TENANCY_MODE=single
# NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT=

View file

@ -9,6 +9,7 @@ ARG SOURCEBOT_VERSION
# @see: https://posthog.com/tutorials/api-capture-events#authenticating-with-the-project-api-key
ARG POSTHOG_PAPIK
ARG SENTRY_ENVIRONMENT
ARG SOURCEBOT_CLOUD_ENVIRONMENT
FROM node:20-alpine3.19 AS node-alpine
FROM golang:1.23.4-alpine3.19 AS go-alpine
@ -52,9 +53,11 @@ ARG POSTHOG_PAPIK
ENV NEXT_PUBLIC_POSTHOG_PAPIK=$POSTHOG_PAPIK
ARG SENTRY_ENVIRONMENT
ENV NEXT_PUBLIC_SENTRY_ENVIRONMENT=$SENTRY_ENVIRONMENT
ARG SOURCEBOT_CLOUD_ENVIRONMENT
ENV NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT=$SOURCEBOT_CLOUD_ENVIRONMENT
# Local args
ARG SENTRY_WEBAPP_DSN
ENV NEXT_PUBLIC_SENTRY_WEBAPP_DSN=$SENTRY_WEBAPP_DSN
ARG NEXT_PUBLIC_SENTRY_WEBAPP_DSN
ENV NEXT_PUBLIC_SENTRY_WEBAPP_DSN=$NEXT_PUBLIC_SENTRY_WEBAPP_DSN
# -----------
RUN apk add --no-cache libc6-compat
@ -104,6 +107,8 @@ ARG SOURCEBOT_VERSION
ENV SOURCEBOT_VERSION=$SOURCEBOT_VERSION
ARG POSTHOG_PAPIK
ENV POSTHOG_PAPIK=$POSTHOG_PAPIK
ARG SENTRY_ENVIRONMENT
ENV SENTRY_ENVIRONMENT=$SENTRY_ENVIRONMENT
# Local args
# -----------

View file

@ -32,6 +32,7 @@ import { useSession } from "next-auth/react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { signOut } from "next-auth/react"
import { env } from "@/env.mjs";
import posthog from "posthog-js";
interface SettingsDropdownProps {
menuButtonClassName?: string;
@ -94,7 +95,9 @@ export const SettingsDropdown = ({
onClick={() => {
signOut({
redirectTo: "/login",
});
}).then(() => {
posthog.reset();
})
}}
>
<LogOut className="mr-2 h-4 w-4" />

View file

@ -1,6 +1,6 @@
'use client';
import { useEffect, useState } from "react";
import { useState } from "react";
import { CodeHostType } from "@/lib/utils";
import { getCodeHostIcon } from "@/lib/utils";
import {
@ -15,8 +15,6 @@ import { OnboardingSteps } from "@/lib/constants";
import { BackButton } from "./onboardBackButton";
import { CodeHostIconButton } from "../../components/codeHostIconButton";
import useCaptureEvent from "@/hooks/useCaptureEvent";
import { useSession } from "next-auth/react";
import posthog from "posthog-js";
import SecurityCard from "@/app/components/securityCard";
interface ConnectCodeHostProps {
@ -27,18 +25,6 @@ interface ConnectCodeHostProps {
export const ConnectCodeHost = ({ nextStep, securityCardEnabled }: ConnectCodeHostProps) => {
const [selectedCodeHost, setSelectedCodeHost] = useState<CodeHostType | null>(null);
const router = useRouter();
const { data: session } = useSession();
// Note: this is currently the first client side page that gets loaded after a user registers. If this changes, we need to update this.
// @nocheckin
useEffect(() => {
if (session?.user) {
posthog.identify(session.user.id, {
email: session.user.email,
name: session.user.name,
});
}
}, [session?.user]);
const onCreated = useCallback(() => {
router.push(`?step=${nextStep}`);

View file

@ -1,5 +1,7 @@
import { LogOutIcon } from "lucide-react";
import { signOut } from "@/auth";
import posthog from "posthog-js";
interface LogoutEscapeHatchProps {
className?: string;
}
@ -14,6 +16,8 @@ export const LogoutEscapeHatch = ({
"use server";
await signOut({
redirectTo: "/login",
}).then(() => {
posthog.reset();
});
}}
>

View file

@ -13,6 +13,12 @@ import { SourcebotLogo } from "@/app/components/sourcebotLogo";
import { TextSeparator } from "@/app/components/textSeparator";
import useCaptureEvent from "@/hooks/useCaptureEvent";
import DemoCard from "@/app/[domain]/onboard/components/demoCard";
import Link from "next/link";
import { env } from "@/env.mjs";
const TERMS_OF_SERVICE_URL = "https://sourcebot.dev/terms";
const PRIVACY_POLICY_URL = "https://sourcebot.dev/privacy";
interface LoginFormProps {
callbackUrl?: string;
error?: string;
@ -98,6 +104,9 @@ export const LoginForm = ({ callbackUrl, error, enabledMethods }: LoginFormProps
]}
/>
</Card>
{env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT !== undefined && (
<p className="text-xs text-muted-foreground mt-8">By signing in, you agree to the <Link className="underline" href={TERMS_OF_SERVICE_URL} target="_blank">Terms of Service</Link> and <Link className="underline" href={PRIVACY_POLICY_URL} target="_blank">Privacy Policy</Link>.</p>
)}
</div>
)
}

View file

@ -5,13 +5,15 @@ import { PostHogProvider as PHProvider } from 'posthog-js/react'
import { usePathname, useSearchParams } from "next/navigation"
import { Suspense, useEffect } from "react"
import { env } from '@/env.mjs'
import { useSession } from 'next-auth/react'
import { captureEvent } from '@/hooks/useCaptureEvent'
// @see: https://posthog.com/docs/libraries/next-js#capturing-pageviews
function PostHogPageView() {
const pathname = usePathname()
const searchParams = useSearchParams()
const posthog = usePostHog()
// Track pageviews
useEffect(() => {
if (pathname && posthog) {
let url = window.origin + pathname
@ -19,7 +21,9 @@ function PostHogPageView() {
url = url + `?${searchParams.toString()}`
}
posthog.capture('$pageview', { '$current_url': url })
captureEvent('$pageview', {
$current_url: url,
});
}
}, [pathname, searchParams, posthog])
@ -32,34 +36,55 @@ interface PostHogProviderProps {
}
export function PostHogProvider({ children, disabled }: PostHogProviderProps) {
const { data: session } = useSession();
useEffect(() => {
if (!disabled && env.NEXT_PUBLIC_POSTHOG_PAPIK) {
console.debug(`PostHog telemetry enabled. Cloud environment: ${env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT}`);
posthog.init(env.NEXT_PUBLIC_POSTHOG_PAPIK, {
// @see next.config.mjs for path rewrites to the "/ingest" route.
api_host: "/ingest",
person_profiles: 'identified_only',
capture_pageview: false,
autocapture: false,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sanitize_properties: (properties: Record<string, any>, _event: string) => {
// https://posthog.com/docs/libraries/js#config
if (properties['$current_url']) {
properties['$current_url'] = null;
}
if (properties['$ip']) {
properties['$ip'] = null;
}
return properties;
}
// In self-hosted mode, we don't want to capture the following
// default properties.
// @see: https://posthog.com/docs/data/events#default-properties
property_denylist: env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT === undefined ? [
'$current_url',
'$pathname',
'$session_entry_url',
'$session_entry_host',
'$session_entry_pathname',
'$session_entry_referrer',
'$session_entry_referring_domain',
'$referrer',
'$referring_domain',
'$ip',
] : []
});
} else {
console.log("PostHog telemetry disabled");
console.debug("PostHog telemetry disabled");
}
}, [disabled])
}, [disabled]);
useEffect(() => {
if (!session) {
return;
}
// Only identify the user if we are running in a cloud environment.
if (env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT !== undefined) {
posthog.identify(session.user.id, {
email: session.user.email,
name: session.user.name,
});
}
}, [session]);
return (
<PHProvider client={posthog}>
{/* @see: https://github.com/vercel/next.js/issues/51581 */}
<Suspense fallback={null}>
<PostHogPageView />
</Suspense>

View file

@ -50,8 +50,10 @@ export const env = createEnv({
// Misc UI flags
SECURITY_CARD_ENABLED: booleanSchema.default('false'),
},
// @NOTE: Make sure you destructure all client variables in the
// `experimental__runtimeEnv` block below.
// @NOTE: Please make sure of the following:
// - Make sure you destructure all client variables in
// the `experimental__runtimeEnv` block below.
// - Update the Dockerfile to pass these variables as build-args.
client: {
// PostHog
NEXT_PUBLIC_POSTHOG_PAPIK: z.string().optional(),
@ -59,12 +61,15 @@ export const env = createEnv({
// Misc
NEXT_PUBLIC_SOURCEBOT_VERSION: z.string().default('unknown'),
NEXT_PUBLIC_POLLING_INTERVAL_MS: numberSchema.default(5000),
NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT: z.enum(["dev", "staging", "prod"]).optional(),
},
// For Next.js >= 13.4.4, you only need to destructure client variables:
experimental__runtimeEnv: {
NEXT_PUBLIC_POSTHOG_PAPIK: process.env.NEXT_PUBLIC_POSTHOG_PAPIK,
NEXT_PUBLIC_SOURCEBOT_VERSION: process.env.NEXT_PUBLIC_SOURCEBOT_VERSION,
NEXT_PUBLIC_POLLING_INTERVAL_MS: process.env.NEXT_PUBLIC_POLLING_INTERVAL_MS,
NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT: process.env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT,
},
skipValidation: process.env.SKIP_ENV_VALIDATION === "1",
emptyStringAsUndefined: true,

View file

@ -244,6 +244,10 @@ export type PosthogEventMap = {
wa_demo_card_click: {},
wa_demo_try_card_pressed: {},
wa_share_link_created: {},
//////////////////////////////////////////////////////////////////
$pageview: {
$current_url: string,
},
}
export type PosthogEvent = keyof PosthogEventMap;